Removed old TeeWorlds repo
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
# The application settings for Android libSDL port
|
||||
AppSettingVersion=17
|
||||
LibSdlVersion=1.2
|
||||
AppName="TeeWorlds"
|
||||
AppFullName=com.teeworlds
|
||||
ScreenOrientation=h
|
||||
InhibitSuspend=n
|
||||
AppDataDownloadUrl="Game data|teeworlds.zip"
|
||||
VideoDepthBpp=16
|
||||
NeedDepthBuffer=n
|
||||
NeedStencilBuffer=n
|
||||
NeedGles2=n
|
||||
SwVideoMode=n
|
||||
SdlVideoResize=n
|
||||
SdlVideoResizeKeepAspect=n
|
||||
CompatibilityHacks=n
|
||||
CompatibilityHacksStaticInit=n
|
||||
CompatibilityHacksTextInputEmulatesHwKeyboard=n
|
||||
AppUsesMouse=y
|
||||
AppNeedsTwoButtonMouse=y
|
||||
ShowMouseCursor=n
|
||||
ForceRelativeMouseMode=y
|
||||
AppNeedsArrowKeys=y
|
||||
AppNeedsTextInput=y
|
||||
AppUsesJoystick=y
|
||||
AppHandlesJoystickSensitivity=n
|
||||
AppUsesMultitouch=n
|
||||
NonBlockingSwapBuffers=n
|
||||
RedefinedKeys="SPACE RETURN LEFT RIGHT LSHIFT ESCAPE RSHIFT LSHIFT"
|
||||
AppTouchscreenKeyboardKeysAmount=6
|
||||
AppTouchscreenKeyboardKeysAmountAutoFire=0
|
||||
RedefinedKeysScreenKb="SPACE RETURN LEFT RIGHT RSHIFT LSHIFT"
|
||||
StartupMenuButtonTimeout=3000
|
||||
HiddenMenuOptions='OptionalDownloadConfig DisplaySizeConfig'
|
||||
FirstStartMenuOptions=''
|
||||
MultiABI=y
|
||||
AppVersionCode=5207
|
||||
AppVersionName="0.5.2.07"
|
||||
ResetSdlConfigForThisVersion=n
|
||||
DeleteFilesOnUpgrade="%"
|
||||
CompiledLibraries="sdl_image freetype"
|
||||
CustomBuildScript=n
|
||||
AppCflags='-O3'
|
||||
AppLdflags=''
|
||||
AppSubdirsBuild=''
|
||||
AppCmdline=''
|
||||
ReadmeText='^You may press "Home" now - the data will be downloaded in background'
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,28 +0,0 @@
|
||||
Copyright (C) 2007-2010 Magnus Auvinen
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
IMPORTANT NOTE! The source under src/engine/external are stripped
|
||||
libraries with their own licenses. Mostly BSD or zlib/libpng license but
|
||||
check the individual libraries.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
With that being said, contact us if there is anything you want to do
|
||||
that the license does not premit.
|
||||
@@ -1,10 +0,0 @@
|
||||
Copyright (c) 2010 Magnus Auvinen
|
||||
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
|
||||
Please visit http://www.teeworlds.com for up-to-date information about
|
||||
the game, including new versions, custom maps and much more.
|
||||
@@ -1,134 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef BASELIB_FILE_CONFIG_H
|
||||
#define BASELIB_FILE_CONFIG_H
|
||||
|
||||
/*
|
||||
this file detected the family, platform and architecture
|
||||
to compile for.
|
||||
*/
|
||||
|
||||
/* platforms */
|
||||
|
||||
/* windows Family */
|
||||
#if defined(WIN64) || defined(_WIN64)
|
||||
/* Hmm, is this IA64 or x86-64? */
|
||||
#define CONF_FAMILY_WINDOWS 1
|
||||
#define CONF_FAMILY_STRING "windows"
|
||||
#define CONF_PLATFORM_WIN64 1
|
||||
#define CONF_PLATFORM_STRING "win64"
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__)
|
||||
#define CONF_FAMILY_WINDOWS 1
|
||||
#define CONF_FAMILY_STRING "windows"
|
||||
#define CONF_PLATFORM_WIN32 1
|
||||
#define CONF_PLATFORM_STRING "win32"
|
||||
#endif
|
||||
|
||||
/* unix family */
|
||||
#if defined(__FreeBSD__)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFORM_FREEBSD 1
|
||||
#define CONF_PLATFORM_STRING "freebsd"
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFORM_OPENBSD 1
|
||||
#define CONF_PLATFORM_STRING "openbsd"
|
||||
#endif
|
||||
|
||||
#if defined(__LINUX__) || defined(__linux__)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFORM_LINUX 1
|
||||
#define CONF_PLATFORM_STRING "linux"
|
||||
#endif
|
||||
|
||||
#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFORM_MACOSX 1
|
||||
#define CONF_PLATFORM_STRING "macosx"
|
||||
#endif
|
||||
|
||||
#if defined(__sun)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFROM_SOLARIS 1
|
||||
#define CONF_PLATFORM_STRING "solaris"
|
||||
#endif
|
||||
|
||||
/* beos family */
|
||||
#if defined(__BeOS) || defined(__BEOS__)
|
||||
#define CONF_FAMILY_BEOS 1
|
||||
#define CONF_FAMILY_STRING "beos"
|
||||
#define CONF_PLATFORM_BEOS 1
|
||||
#define CONF_PLATFORM_STRING "beos"
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID)
|
||||
#define CONF_FAMILY_UNIX 1
|
||||
#define CONF_FAMILY_STRING "unix"
|
||||
#define CONF_PLATFORM_LINUX 1
|
||||
#define CONF_PLATFORM_STRING "android"
|
||||
#endif
|
||||
|
||||
|
||||
/* architectures */
|
||||
#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32)
|
||||
#define CONF_ARCH_IA32 1
|
||||
#define CONF_ARCH_STRING "ia32"
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
|
||||
#if defined(__ia64__)
|
||||
#define CONF_ARCH_IA64 1
|
||||
#define CONF_ARCH_STRING "ia64"
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
|
||||
#if defined(__amd64__) || defined(__x86_64__)
|
||||
#define CONF_ARCH_AMD64 1
|
||||
#define CONF_ARCH_STRING "amd64"
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc__) || defined(__ppc__)
|
||||
#define CONF_ARCH_PPC 1
|
||||
#define CONF_ARCH_STRING "ppc"
|
||||
#define CONF_ARCH_ENDIAN_BIG 1
|
||||
#endif
|
||||
|
||||
#if defined(__sparc__)
|
||||
#define CONF_ARCH_SPARC 1
|
||||
#define CONF_ARCH_STRING "sparc"
|
||||
#define CONF_ARCH_ENDIAN_BIG 1
|
||||
#endif
|
||||
|
||||
#if defined(__ARMEB__)
|
||||
#define CONF_ARCH_ARM 1
|
||||
#define CONF_ARCH_STRING "arm"
|
||||
#define CONF_ARCH_ENDIAN_BIG 1
|
||||
#endif
|
||||
|
||||
#if defined(__ARMEL__)
|
||||
#define CONF_ARCH_ARM 1
|
||||
#define CONF_ARCH_STRING "arm"
|
||||
#define CONF_ARCH_ENDIAN_LITTLE 1
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef CONF_FAMILY_STRING
|
||||
#define CONF_FAMILY_STRING "unknown"
|
||||
#endif
|
||||
|
||||
#ifndef CONF_PLATFORM_STRING
|
||||
#define CONF_PLATFORM_STRING "unknown"
|
||||
#endif
|
||||
|
||||
#ifndef CONF_ARCH_STRING
|
||||
#define CONF_ARCH_STRING "unknown"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,68 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef BASE_MATH_H
|
||||
#define BASE_MATH_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
template <typename T>
|
||||
inline T clamp(T val, T min, T max)
|
||||
{
|
||||
if(val < min)
|
||||
return min;
|
||||
if(val > max)
|
||||
return max;
|
||||
return val;
|
||||
}
|
||||
|
||||
inline float sign(float f)
|
||||
{
|
||||
return f<0.0f?-1.0f:1.0f;
|
||||
}
|
||||
|
||||
inline int round(float f)
|
||||
{
|
||||
if(f > 0)
|
||||
return (int)(f+0.5f);
|
||||
return (int)(f-0.5f);
|
||||
}
|
||||
|
||||
template<typename T, typename TB>
|
||||
inline T mix(const T a, const T b, TB amount)
|
||||
{
|
||||
return a + (b-a)*amount;
|
||||
}
|
||||
|
||||
inline float frandom() { return rand()/(float)(RAND_MAX); }
|
||||
|
||||
// float to fixed
|
||||
inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
|
||||
inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
|
||||
|
||||
class fxp
|
||||
{
|
||||
int value;
|
||||
public:
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
fxp &operator = (int v) { value = v<<10; return *this; }
|
||||
fxp &operator = (float v) { value = (int)(v*(float)(1<<10)); return *this; }
|
||||
operator float() const { return value/(float)(1<<10); }
|
||||
};
|
||||
|
||||
class tune_param
|
||||
{
|
||||
int value;
|
||||
public:
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
tune_param &operator = (int v) { value = (int)(v*100.0f); return *this; }
|
||||
tune_param &operator = (float v) { value = (int)(v*100.0f); return *this; }
|
||||
operator float() const { return value/100.0f; }
|
||||
};
|
||||
|
||||
const float pi = 3.1415926535897932384626433f;
|
||||
|
||||
template <typename T> inline T min(T a, T b) { return a<b?a:b; }
|
||||
template <typename T> inline T max(T a, T b) { return a>b?a:b; }
|
||||
|
||||
#endif // BASE_MATH_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,954 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
/*
|
||||
Title: OS Abstraction
|
||||
*/
|
||||
|
||||
#ifndef BASE_SYSTEM_H
|
||||
#define BASE_SYSTEM_H
|
||||
|
||||
#include "detect.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Group: Debug */
|
||||
/*
|
||||
Function: dbg_assert
|
||||
Breaks into the debugger based on a test.
|
||||
|
||||
Parameters:
|
||||
test - Result of the test.
|
||||
msg - Message that should be printed if the test fails.
|
||||
|
||||
Remarks:
|
||||
Does nothing in release version of the library.
|
||||
|
||||
See Also:
|
||||
<dbg_break>
|
||||
*/
|
||||
void dbg_assert(int test, const char *msg);
|
||||
#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg)
|
||||
void dbg_assert_imp(const char *filename, int line, int test, const char *msg);
|
||||
|
||||
/*
|
||||
Function: dbg_break
|
||||
Breaks into the debugger.
|
||||
|
||||
Remarks:
|
||||
Does nothing in release version of the library.
|
||||
|
||||
See Also:
|
||||
<dbg_assert>
|
||||
*/
|
||||
void dbg_break();
|
||||
|
||||
/*
|
||||
Function: dbg_msg
|
||||
|
||||
Prints a debug message.
|
||||
|
||||
Parameters:
|
||||
sys - A string that describes what system the message belongs to
|
||||
fmt - A printf styled format string.
|
||||
|
||||
Remarks:
|
||||
Does nothing in relase version of the library.
|
||||
|
||||
See Also:
|
||||
<dbg_assert>
|
||||
*/
|
||||
void dbg_msg(const char *sys, const char *fmt, ...);
|
||||
|
||||
/* Group: Memory */
|
||||
|
||||
/*
|
||||
Function: mem_alloc
|
||||
Allocates memory.
|
||||
|
||||
Parameters:
|
||||
size - Size of the needed block.
|
||||
alignment - Alignment for the block.
|
||||
|
||||
Returns:
|
||||
Returns a pointer to the newly allocated block. Returns a
|
||||
null pointer if the memory couldn't be allocated.
|
||||
|
||||
Remarks:
|
||||
- Passing 0 to size will allocated the smallest amount possible
|
||||
and return a unique pointer.
|
||||
|
||||
See Also:
|
||||
<mem_free>
|
||||
*/
|
||||
void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment);
|
||||
#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a))
|
||||
|
||||
/*
|
||||
Function: mem_free
|
||||
Frees a block allocated through <mem_alloc>.
|
||||
|
||||
Remarks:
|
||||
- In the debug version of the library the function will assert if
|
||||
a non-valid block is passed, like a null pointer or a block that
|
||||
isn't allocated.
|
||||
|
||||
See Also:
|
||||
<mem_alloc>
|
||||
*/
|
||||
void mem_free(void *block);
|
||||
|
||||
/*
|
||||
Function: mem_copy
|
||||
Copies a a memory block.
|
||||
|
||||
Parameters:
|
||||
dest - Destination.
|
||||
source - Source to copy.
|
||||
size - Size of the block to copy.
|
||||
|
||||
Remarks:
|
||||
- This functions DOES NOT handles cases where source and
|
||||
destination is overlapping.
|
||||
|
||||
See Also:
|
||||
<mem_move>
|
||||
*/
|
||||
void mem_copy(void *dest, const void *source, unsigned size);
|
||||
|
||||
/*
|
||||
Function: mem_move
|
||||
Copies a a memory block
|
||||
|
||||
Parameters:
|
||||
dest - Destination
|
||||
source - Source to copy
|
||||
size - Size of the block to copy
|
||||
|
||||
Remarks:
|
||||
- This functions handles cases where source and destination
|
||||
is overlapping
|
||||
|
||||
See Also:
|
||||
<mem_copy>
|
||||
*/
|
||||
void mem_move(void *dest, const void *source, unsigned size);
|
||||
|
||||
/*
|
||||
Function: mem_zero
|
||||
Sets a complete memory block to 0
|
||||
|
||||
Parameters:
|
||||
block - Pointer to the block to zero out
|
||||
size - Size of the block
|
||||
*/
|
||||
void mem_zero(void *block, unsigned size);
|
||||
|
||||
/*
|
||||
Function: mem_comp
|
||||
Compares two blocks of memory
|
||||
|
||||
Parameters:
|
||||
a - First block of data
|
||||
b - Second block of data
|
||||
size - Size of the data to compare
|
||||
|
||||
Returns:
|
||||
<0 - Block a is lesser then block b
|
||||
0 - Block a is equal to block b
|
||||
>0 - Block a is greater then block b
|
||||
*/
|
||||
int mem_comp(const void *a, const void *b, int size);
|
||||
|
||||
/*
|
||||
Function: mem_check
|
||||
Validates the heap
|
||||
Will trigger a assert if memory has failed.
|
||||
*/
|
||||
int mem_check_imp();
|
||||
#define mem_check() dbg_assert_imp(__FILE__, __LINE__, mem_check_imp(), "Memory check failed")
|
||||
|
||||
/* Group: File IO */
|
||||
enum {
|
||||
IOFLAG_READ = 1,
|
||||
IOFLAG_WRITE = 2,
|
||||
IOFLAG_RANDOM = 4,
|
||||
|
||||
IOSEEK_START = 0,
|
||||
IOSEEK_CUR = 1,
|
||||
IOSEEK_END = 2
|
||||
};
|
||||
|
||||
typedef struct IOINTERNAL *IOHANDLE;
|
||||
|
||||
/*
|
||||
Function: io_open
|
||||
Opens a file.
|
||||
|
||||
Parameters:
|
||||
filename - File to open.
|
||||
flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM.
|
||||
|
||||
Returns:
|
||||
Returns a handle to the file on success and 0 on failure.
|
||||
|
||||
*/
|
||||
IOHANDLE io_open(const char *filename, int flags);
|
||||
|
||||
/*
|
||||
Function: io_read
|
||||
Reads data into a buffer from a file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file to read data from.
|
||||
buffer - Pointer to the buffer that will recive the data.
|
||||
size - Number of bytes to read from the file.
|
||||
|
||||
Returns:
|
||||
Number of bytes read.
|
||||
|
||||
*/
|
||||
unsigned io_read(IOHANDLE io, void *buffer, unsigned size);
|
||||
|
||||
/*
|
||||
Function: io_skip
|
||||
Skips data in a file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
size - Number of bytes to skip.
|
||||
|
||||
Returns:
|
||||
Number of bytes skipped.
|
||||
*/
|
||||
unsigned io_skip(IOHANDLE io, unsigned size);
|
||||
|
||||
/*
|
||||
Function: io_write
|
||||
Writes data from a buffer to file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
buffer - Pointer to the data that should be written.
|
||||
size - Number of bytes to write.
|
||||
|
||||
Returns:
|
||||
Number of bytes written.
|
||||
*/
|
||||
unsigned io_write(IOHANDLE io, const void *buffer, unsigned size);
|
||||
|
||||
/*
|
||||
Function: io_seek
|
||||
Seeks to a specified offset in the file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
offset - Offset from pos to stop.
|
||||
origin - Position to start searching from.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success.
|
||||
*/
|
||||
int io_seek(IOHANDLE io, int offset, int origin);
|
||||
|
||||
/*
|
||||
Function: io_tell
|
||||
Gets the current position in the file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
|
||||
Returns:
|
||||
Returns the current position. -1L if an error occured.
|
||||
*/
|
||||
long int io_tell(IOHANDLE io);
|
||||
|
||||
/*
|
||||
Function: io_length
|
||||
Gets the total length of the file. Resetting cursor to the beginning
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
|
||||
Returns:
|
||||
Returns the total size. -1L if an error occured.
|
||||
*/
|
||||
long int io_length(IOHANDLE io);
|
||||
|
||||
/*
|
||||
Function: io_close
|
||||
Closes a file.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success.
|
||||
*/
|
||||
int io_close(IOHANDLE io);
|
||||
|
||||
/*
|
||||
Function: io_flush
|
||||
Empties all buffers and writes all pending data.
|
||||
|
||||
Parameters:
|
||||
io - Handle to the file.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success.
|
||||
*/
|
||||
int io_flush(IOHANDLE io);
|
||||
|
||||
|
||||
/*
|
||||
Function: io_stdin
|
||||
Returns an <IOHANDLE> to the standard input.
|
||||
*/
|
||||
IOHANDLE io_stdin();
|
||||
|
||||
/*
|
||||
Function: io_stdout
|
||||
Returns an <IOHANDLE> to the standard output.
|
||||
*/
|
||||
IOHANDLE io_stdout();
|
||||
|
||||
/*
|
||||
Function: io_stderr
|
||||
Returns an <IOHANDLE> to the standard error.
|
||||
*/
|
||||
IOHANDLE io_stderr();
|
||||
|
||||
|
||||
/* Group: Threads */
|
||||
|
||||
/*
|
||||
Function: thread_sleep
|
||||
Suspends the current thread for a given period.
|
||||
|
||||
Parameters:
|
||||
milliseconds - Number of milliseconds to sleep.
|
||||
*/
|
||||
void thread_sleep(int milliseconds);
|
||||
|
||||
/*
|
||||
Function: thread_create
|
||||
Creates a new thread.
|
||||
|
||||
Parameters:
|
||||
threadfunc - Entry point for the new thread.
|
||||
user - Pointer to pass to the thread.
|
||||
|
||||
*/
|
||||
void *thread_create(void (*threadfunc)(void *), void *user);
|
||||
|
||||
/*
|
||||
Function: thread_wait
|
||||
Waits for a thread to be done or destroyed.
|
||||
|
||||
Parameters:
|
||||
thread - Thread to wait for.
|
||||
*/
|
||||
void thread_wait(void *thread);
|
||||
|
||||
/*
|
||||
Function: thread_destoy
|
||||
Destroys a thread.
|
||||
|
||||
Parameters:
|
||||
thread - Thread to destroy.
|
||||
*/
|
||||
void thread_destroy(void *thread);
|
||||
|
||||
/*
|
||||
Function: thread_yeild
|
||||
Yeild the current threads execution slice.
|
||||
*/
|
||||
void thread_yield();
|
||||
|
||||
|
||||
/* Group: Locks */
|
||||
typedef void* LOCK;
|
||||
|
||||
LOCK lock_create();
|
||||
void lock_destroy(LOCK lock);
|
||||
|
||||
int lock_try(LOCK lock);
|
||||
void lock_wait(LOCK lock);
|
||||
void lock_release(LOCK lock);
|
||||
|
||||
/* Group: Timer */
|
||||
#ifdef __GNUC__
|
||||
/* if compiled with -pedantic-errors it will complain about long
|
||||
not being a C90 thing.
|
||||
*/
|
||||
__extension__ typedef long long int64;
|
||||
#else
|
||||
typedef long long int64;
|
||||
#endif
|
||||
/*
|
||||
Function: time_get
|
||||
Fetches a sample from a high resolution timer.
|
||||
|
||||
Returns:
|
||||
Current value of the timer.
|
||||
|
||||
Remarks:
|
||||
To know how fast the timer is ticking, see <time_freq>.
|
||||
*/
|
||||
int64 time_get();
|
||||
|
||||
/*
|
||||
Function: time_freq
|
||||
Returns the frequency of the high resolution timer.
|
||||
|
||||
Returns:
|
||||
Returns the frequency of the high resolution timer.
|
||||
*/
|
||||
int64 time_freq();
|
||||
|
||||
/*
|
||||
Function: time_timestamp
|
||||
Retrives the current time as a UNIX timestamp
|
||||
|
||||
Returns:
|
||||
The time as a UNIX timestamp
|
||||
*/
|
||||
unsigned time_timestamp();
|
||||
|
||||
/* Group: Network General */
|
||||
typedef int NETSOCKET;
|
||||
enum
|
||||
{
|
||||
NETSOCKET_INVALID = -1,
|
||||
|
||||
NETTYPE_INVALID = 0,
|
||||
NETTYPE_IPV4 = 1,
|
||||
NETTYPE_IPV6 = 2,
|
||||
NETTYPE_ALL = ~0
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int type;
|
||||
unsigned char ip[16];
|
||||
unsigned short port;
|
||||
} NETADDR;
|
||||
|
||||
/*
|
||||
Function: net_init
|
||||
Initiates network functionallity.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success,
|
||||
|
||||
Remarks:
|
||||
You must call this function before using any other network
|
||||
functions.
|
||||
*/
|
||||
int net_init();
|
||||
|
||||
/*
|
||||
Function: net_host_lookup
|
||||
Does a hostname lookup by name and fills out the passed
|
||||
NETADDR struct with the recieved details.
|
||||
|
||||
Returns:
|
||||
0 on success.
|
||||
*/
|
||||
int net_host_lookup(const char *hostname, NETADDR *addr, int types);
|
||||
|
||||
/*
|
||||
Function: net_addr_comp
|
||||
Compares two network addresses.
|
||||
|
||||
Parameters:
|
||||
a - Address to compare
|
||||
b - Address to compare to.
|
||||
|
||||
Returns:
|
||||
<0 - Address a is lesser then address b
|
||||
0 - Address a is equal to address b
|
||||
>0 - Address a is greater then address b
|
||||
*/
|
||||
int net_addr_comp(const NETADDR *a, const NETADDR *b);
|
||||
|
||||
/*
|
||||
Function: net_addr_str
|
||||
Turns a network address into a representive string.
|
||||
|
||||
Parameters:
|
||||
addr - Address to turn into a string.
|
||||
string - Buffer to fill with the string.
|
||||
max_length - Maximum size of the string.
|
||||
|
||||
Remarks:
|
||||
- The string will always be zero terminated
|
||||
|
||||
*/
|
||||
void net_addr_str(const NETADDR *addr, char *string, int max_length);
|
||||
|
||||
/*
|
||||
Function: net_addr_from_str
|
||||
Turns string into a network address.
|
||||
|
||||
Returns:
|
||||
0 on success
|
||||
|
||||
Parameters:
|
||||
addr - Address to fill in.
|
||||
string - String to parse.
|
||||
*/
|
||||
int net_addr_from_str(NETADDR *addr, const char *string);
|
||||
|
||||
/* Group: Network UDP */
|
||||
|
||||
/*
|
||||
Function: net_udp_create
|
||||
Creates a UDP socket and binds it to a port.
|
||||
|
||||
Parameters:
|
||||
bindaddr - Address to bind the socket to.
|
||||
|
||||
Returns:
|
||||
On success it returns an handle to the socket. On failure it
|
||||
returns NETSOCKET_INVALID.
|
||||
*/
|
||||
NETSOCKET net_udp_create(NETADDR bindaddr);
|
||||
|
||||
/*
|
||||
Function: net_udp_send
|
||||
Sends a packet over an UDP socket.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to use.
|
||||
addr - Where to send the packet.
|
||||
data - Pointer to the packet data to send.
|
||||
size - Size of the packet.
|
||||
|
||||
Returns:
|
||||
On success it returns the number of bytes sent. Returns -1
|
||||
on error.
|
||||
*/
|
||||
int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size);
|
||||
|
||||
/*
|
||||
Function: net_udp_recv
|
||||
Recives a packet over an UDP socket.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to use.
|
||||
addr - Pointer to an NETADDR that will recive the address.
|
||||
data - Pointer to a buffer that will recive the data.
|
||||
maxsize - Maximum size to recive.
|
||||
|
||||
Returns:
|
||||
On success it returns the number of bytes recived. Returns -1
|
||||
on error.
|
||||
*/
|
||||
int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize);
|
||||
|
||||
/*
|
||||
Function: net_udp_close
|
||||
Closes an UDP socket.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to close.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success. -1 on error.
|
||||
*/
|
||||
int net_udp_close(NETSOCKET sock);
|
||||
|
||||
|
||||
/* Group: Network TCP */
|
||||
|
||||
/*
|
||||
Function: net_tcp_create
|
||||
Creates a TCP socket.
|
||||
|
||||
Parameters:
|
||||
bindaddr - Address to bind the socket to.
|
||||
|
||||
Returns:
|
||||
On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID.
|
||||
*/
|
||||
NETSOCKET net_tcp_create(const NETADDR *a);
|
||||
|
||||
/*
|
||||
Function: net_tcp_listen
|
||||
Makes the socket start listening for new connections.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to start listen to.
|
||||
backlog - Size of the queue of incomming connections to keep.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success.
|
||||
*/
|
||||
int net_tcp_listen(NETSOCKET sock, int backlog);
|
||||
|
||||
/*
|
||||
Function: net_tcp_accept
|
||||
Polls a listning socket for a new connection.
|
||||
|
||||
Parameters:
|
||||
sock - Listning socket to poll.
|
||||
new_sock - Pointer to a socket to fill in with the new socket.
|
||||
addr - Pointer to an address that will be filled in the remote address (optional, can be NULL).
|
||||
|
||||
Returns:
|
||||
Returns a non-negative integer on success. Negative integer on failure.
|
||||
*/
|
||||
int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *addr);
|
||||
|
||||
/*
|
||||
Function: net_tcp_connect
|
||||
Connects one socket to another.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to connect.
|
||||
addr - Address to connect to.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success.
|
||||
|
||||
*/
|
||||
int net_tcp_connect(NETSOCKET sock, const NETADDR *addr);
|
||||
|
||||
/*
|
||||
Function: net_tcp_send
|
||||
Sends data to a TCP stream.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to send data to.
|
||||
data - Pointer to the data to send.
|
||||
size - Size of the data to send.
|
||||
|
||||
Returns:
|
||||
Number of bytes sent. Negative value on failure.
|
||||
*/
|
||||
int net_tcp_send(NETSOCKET sock, const void *data, int size);
|
||||
|
||||
/*
|
||||
Function: net_tcp_recv
|
||||
Recvives data from a TCP stream.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to recvive data from.
|
||||
data - Pointer to a buffer to write the data to
|
||||
max_size - Maximum of data to write to the buffer.
|
||||
|
||||
Returns:
|
||||
Number of bytes recvived. Negative value on failure. When in
|
||||
non-blocking mode, it returns 0 when there is no more data to
|
||||
be fetched.
|
||||
*/
|
||||
int net_tcp_recv(NETSOCKET sock, void *data, int maxsize);
|
||||
|
||||
/*
|
||||
Function: net_tcp_close
|
||||
Closes a TCP socket.
|
||||
|
||||
Parameters:
|
||||
sock - Socket to close.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success. Negative value on failure.
|
||||
*/
|
||||
int net_tcp_close(NETSOCKET sock);
|
||||
|
||||
/* Group: Strings */
|
||||
|
||||
/*
|
||||
Function: str_append
|
||||
Appends a string to another.
|
||||
|
||||
Parameters:
|
||||
dst - Pointer to a buffer that contains a string.
|
||||
src - String to append.
|
||||
dst_size - Size of the buffer of the dst string.
|
||||
|
||||
Remarks:
|
||||
- The strings are treated as zero-termineted strings.
|
||||
- Garantees that dst string will contain zero-termination.
|
||||
*/
|
||||
void str_append(char *dst, const char *src, int dst_size);
|
||||
|
||||
/*
|
||||
Function: str_copy
|
||||
Copies a string to another.
|
||||
|
||||
Parameters:
|
||||
dst - Pointer to a buffer that shall recive the string.
|
||||
src - String to be copied.
|
||||
dst_size - Size of the buffer dst.
|
||||
|
||||
Remarks:
|
||||
- The strings are treated as zero-termineted strings.
|
||||
- Garantees that dst string will contain zero-termination.
|
||||
*/
|
||||
void str_copy(char *dst, const char *src, int dst_size);
|
||||
|
||||
/*
|
||||
Function: str_length
|
||||
Returns the length of a zero terminated string.
|
||||
|
||||
Parameters:
|
||||
str - Pointer to the string.
|
||||
|
||||
Returns:
|
||||
Length of string in bytes excluding the zero termination.
|
||||
*/
|
||||
int str_length(const char *str);
|
||||
|
||||
/*
|
||||
Function: str_format
|
||||
Performs printf formating into a buffer.
|
||||
|
||||
Parameters:
|
||||
buffer - Pointer to the buffer to recive the formated string.
|
||||
buffer_size - Size of the buffer.
|
||||
format - printf formating string.
|
||||
... - Parameters for the formating.
|
||||
|
||||
Remarks:
|
||||
- See the C manual for syntax for the printf formating string.
|
||||
- The strings are treated as zero-termineted strings.
|
||||
- Garantees that dst string will contain zero-termination.
|
||||
*/
|
||||
void str_format(char *buffer, int buffer_size, const char *format, ...);
|
||||
|
||||
/*
|
||||
Function: str_sanitize_strong
|
||||
Replaces all characters below 32 and above 127 with whitespace.
|
||||
|
||||
Parameters:
|
||||
str - String to sanitize.
|
||||
|
||||
Remarks:
|
||||
- The strings are treated as zero-termineted strings.
|
||||
*/
|
||||
void str_sanitize_strong(char *str);
|
||||
|
||||
/*
|
||||
Function: str_sanitize
|
||||
Replaces all characters below 32 and above 127 with whitespace with
|
||||
exception to \r, \n and \r.
|
||||
|
||||
Parameters:
|
||||
str - String to sanitize.
|
||||
|
||||
Remarks:
|
||||
- The strings are treated as zero-termineted strings.
|
||||
*/
|
||||
void str_sanitize(char *str);
|
||||
|
||||
/*
|
||||
Function: str_comp_nocase
|
||||
Compares to strings case insensitive.
|
||||
|
||||
Parameters:
|
||||
a - String to compare.
|
||||
b - String to compare.
|
||||
|
||||
Returns:
|
||||
<0 - String a is lesser then string b
|
||||
0 - String a is equal to string b
|
||||
>0 - String a is greater then string b
|
||||
|
||||
Remarks:
|
||||
- Only garanted to work with a-z/A-Z.
|
||||
- The strings are treated as zero-termineted strings.
|
||||
*/
|
||||
int str_comp_nocase(const char *a, const char *b);
|
||||
|
||||
/*
|
||||
Function: str_find_nocase
|
||||
Finds a string inside another string case insensitive.
|
||||
|
||||
Parameters:
|
||||
haystack - String to search in
|
||||
needle - String to search for
|
||||
|
||||
Returns:
|
||||
A pointer into haystack where the needle was found.
|
||||
Returns NULL of needle could not be found.
|
||||
|
||||
Remarks:
|
||||
- Only garanted to work with a-z/A-Z.
|
||||
- The strings are treated as zero-termineted strings.
|
||||
*/
|
||||
const char *str_find_nocase(const char *haystack, const char *needle);
|
||||
|
||||
|
||||
/*
|
||||
Function: str_hex
|
||||
Takes a datablock and generates a hexstring of it.
|
||||
|
||||
Parameters:
|
||||
dst - Buffer to fill with hex data
|
||||
dst_size - size of the buffer
|
||||
data - Data to turn into hex
|
||||
data - Size of the data
|
||||
|
||||
Remarks:
|
||||
- The desination buffer will be zero-terminated
|
||||
*/
|
||||
void str_hex(char *dst, int dst_size, const void *data, int data_size);
|
||||
|
||||
/* Group: Filesystem */
|
||||
|
||||
/*
|
||||
Function: fs_listdir
|
||||
Lists the files in a directory
|
||||
|
||||
Parameters:
|
||||
dir - Directory to list
|
||||
cb - Callback function to call for each entry
|
||||
user - Pointer to give to the callback
|
||||
|
||||
Returns:
|
||||
Always returns 0.
|
||||
*/
|
||||
typedef void (*FS_LISTDIR_CALLBACK)(const char *name, int is_dir, void *user);
|
||||
int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, void *user);
|
||||
|
||||
/*
|
||||
Function: fs_makedir
|
||||
Creates a directory
|
||||
|
||||
Parameters:
|
||||
path - Directory to create
|
||||
|
||||
Returns:
|
||||
Returns 0 on success. Negative value on failure.
|
||||
|
||||
Remarks:
|
||||
Does not create several directories if needed. "a/b/c" will result
|
||||
in a failure if b or a does not exist.
|
||||
*/
|
||||
int fs_makedir(const char *path);
|
||||
|
||||
/*
|
||||
Function: fs_storage_path
|
||||
Fetches per user configuration directory.
|
||||
|
||||
Returns:
|
||||
Returns 0 on success. Negative value on failure.
|
||||
|
||||
Remarks:
|
||||
- Returns ~/.appname on UNIX based systems
|
||||
- Returns ~/Library/Applications Support/appname on Mac OS X
|
||||
- Returns %APPDATA%/Appname on Windows based systems
|
||||
*/
|
||||
int fs_storage_path(const char *appname, char *path, int max);
|
||||
|
||||
/*
|
||||
Function: fs_is_dir
|
||||
Checks if directory exists
|
||||
|
||||
Returns:
|
||||
Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
int fs_is_dir(const char *path);
|
||||
|
||||
/*
|
||||
Function: fs_chdir
|
||||
Changes current working directory
|
||||
|
||||
Returns:
|
||||
Returns 0 on success, 1 on failure.
|
||||
*/
|
||||
int fs_chdir(const char *path);
|
||||
|
||||
/*
|
||||
Group: Undocumented
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Function: net_tcp_connect_non_blocking
|
||||
|
||||
DOCTODO: serp
|
||||
*/
|
||||
int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a);
|
||||
|
||||
/*
|
||||
Function: net_tcp_set_non_blocking
|
||||
|
||||
DOCTODO: serp
|
||||
*/
|
||||
int net_tcp_set_non_blocking(NETSOCKET sock);
|
||||
|
||||
/*
|
||||
Function: net_tcp_set_non_blocking
|
||||
|
||||
DOCTODO: serp
|
||||
*/
|
||||
int net_tcp_set_blocking(NETSOCKET sock);
|
||||
|
||||
/*
|
||||
Function: net_errno
|
||||
|
||||
DOCTODO: serp
|
||||
*/
|
||||
int net_errno();
|
||||
|
||||
/*
|
||||
Function: net_would_block
|
||||
|
||||
DOCTODO: serp
|
||||
*/
|
||||
int net_would_block();
|
||||
|
||||
int net_socket_read_wait(NETSOCKET sock, int time);
|
||||
|
||||
void mem_debug_dump();
|
||||
|
||||
void swap_endian(void *data, unsigned elem_size, unsigned num);
|
||||
|
||||
typedef void (*DBG_LOGGER)(const char *line);
|
||||
void dbg_logger(DBG_LOGGER logger);
|
||||
void dbg_logger_stdout();
|
||||
void dbg_logger_debugger();
|
||||
void dbg_logger_file(const char *filename);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int allocated;
|
||||
int active_allocations;
|
||||
int total_allocations;
|
||||
} MEMSTATS;
|
||||
|
||||
const MEMSTATS *mem_stats();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sent_packets;
|
||||
int sent_bytes;
|
||||
int recv_packets;
|
||||
int recv_bytes;
|
||||
} NETSTATS;
|
||||
|
||||
|
||||
void net_stats(NETSTATS *stats);
|
||||
|
||||
int str_isspace(char c);
|
||||
|
||||
|
||||
/*
|
||||
Function: gui_messagebox
|
||||
Display plain OS-dependent message box
|
||||
|
||||
Parameters:
|
||||
title - title of the message box
|
||||
message - text to display
|
||||
*/
|
||||
void gui_messagebox(const char *title, const char *message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,196 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef BASE_VMATH_H
|
||||
#define BASE_VMATH_H
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
template<typename T>
|
||||
class vector2_base
|
||||
{
|
||||
public:
|
||||
union { T x,u; };
|
||||
union { T y,v; };
|
||||
|
||||
vector2_base() {}
|
||||
vector2_base(float nx, float ny)
|
||||
{
|
||||
x = nx;
|
||||
y = ny;
|
||||
}
|
||||
|
||||
vector2_base operator -() const { return vector2_base(-x, -y); }
|
||||
vector2_base operator -(const vector2_base &v) const { return vector2_base(x-v.x, y-v.y); }
|
||||
vector2_base operator +(const vector2_base &v) const { return vector2_base(x+v.x, y+v.y); }
|
||||
vector2_base operator *(const T v) const { return vector2_base(x*v, y*v); }
|
||||
|
||||
const vector2_base &operator =(const vector2_base &v) { x = v.x; y = v.y; return *this; }
|
||||
|
||||
const vector2_base &operator +=(const vector2_base &v) { x += v.x; y += v.y; return *this; }
|
||||
const vector2_base &operator -=(const vector2_base &v) { x -= v.x; y -= v.y; return *this; }
|
||||
const vector2_base &operator *=(const T v) { x *= v; y *= v; return *this; }
|
||||
|
||||
bool operator ==(const vector2_base &v) const { return x == v.x && y == v.y; } //TODO: do this with an eps instead
|
||||
|
||||
operator const T* () { return &x; }
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline T length(const vector2_base<T> &a)
|
||||
{
|
||||
return sqrtf(a.x*a.x + a.y*a.y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T distance(const vector2_base<T> a, const vector2_base<T> &b)
|
||||
{
|
||||
return length(a-b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T dot(const vector2_base<T> a, const vector2_base<T> &b)
|
||||
{
|
||||
return a.x*b.x + a.y*b.y;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline vector2_base<T> normalize(const vector2_base<T> &v)
|
||||
{
|
||||
T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y));
|
||||
return vector2_base<T>(v.x*l, v.y*l);
|
||||
}
|
||||
|
||||
typedef vector2_base<float> vec2;
|
||||
typedef vector2_base<bool> bvec2;
|
||||
typedef vector2_base<int> ivec2;
|
||||
|
||||
template<typename T>
|
||||
inline vector2_base<T> closest_point_on_line(vector2_base<T> line_point0, vector2_base<T> line_point1, vector2_base<T> target_point)
|
||||
{
|
||||
vector2_base<T> c = target_point - line_point0;
|
||||
vector2_base<T> v = (line_point1 - line_point0);
|
||||
v = normalize(v);
|
||||
T d = length(line_point0-line_point1);
|
||||
T t = dot(v, c)/d;
|
||||
return mix(line_point0, line_point1, clamp(t, (T)0, (T)1));
|
||||
/*
|
||||
if (t < 0) t = 0;
|
||||
if (t > 1.0f) return 1.0f;
|
||||
return t;*/
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
template<typename T>
|
||||
class vector3_base
|
||||
{
|
||||
public:
|
||||
union { T x,r,h; };
|
||||
union { T y,g,s; };
|
||||
union { T z,b,v,l; };
|
||||
|
||||
vector3_base() {}
|
||||
vector3_base(float nx, float ny, float nz)
|
||||
{
|
||||
x = nx;
|
||||
y = ny;
|
||||
z = nz;
|
||||
}
|
||||
|
||||
const vector3_base &operator =(const vector3_base &v) { x = v.x; y = v.y; z = v.z; return *this; }
|
||||
|
||||
vector3_base operator -(const vector3_base &v) const { return vector3_base(x-v.x, y-v.y, z-v.z); }
|
||||
vector3_base operator -() const { return vector3_base(-x, -y, -z); }
|
||||
vector3_base operator +(const vector3_base &v) const { return vector3_base(x+v.x, y+v.y, z+v.z); }
|
||||
vector3_base operator *(const T v) const { return vector3_base(x*v, y*v, z*v); }
|
||||
vector3_base operator *(const vector3_base &v) const { return vector3_base(x*v.x, y*v.y, z*v.z); }
|
||||
vector3_base operator /(const T v) const { return vector3_base(x/v, y/v, z/v); }
|
||||
|
||||
const vector3_base &operator +=(const vector3_base &v) { x += v.x; y += v.y; z += v.z; return *this; }
|
||||
const vector3_base &operator -=(const vector3_base &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
|
||||
const vector3_base &operator *=(const T v) { x *= v; y *= v; z *= v; return *this; }
|
||||
|
||||
bool operator ==(const vector3_base &v) const { return x == v.x && y == v.y && z == v.z; } //TODO: do this with an eps instead
|
||||
|
||||
operator const T* () { return &x; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline T length(const vector3_base<T> &a)
|
||||
{
|
||||
return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T distance(const vector3_base<T> &a, const vector3_base<T> &b)
|
||||
{
|
||||
return length(a-b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T dot(const vector3_base<T> &a, const vector3_base<T> &b)
|
||||
{
|
||||
return a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline vector3_base<T> normalize(const vector3_base<T> &v)
|
||||
{
|
||||
T l = (T)(1.0f/sqrtf(v.x*v.x + v.y*v.y + v.z*v.z));
|
||||
return vector3_base<T>(v.x*l, v.y*l, v.z*l);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline vector3_base<T> cross(const vector3_base<T> &a, const vector3_base<T> &b)
|
||||
{
|
||||
return vector3_base<T>(
|
||||
a.y*b.z - a.z*b.y,
|
||||
a.z*b.x - a.x*b.z,
|
||||
a.x*b.y - a.y*b.x);
|
||||
}
|
||||
|
||||
typedef vector3_base<float> vec3;
|
||||
typedef vector3_base<bool> bvec3;
|
||||
typedef vector3_base<int> ivec3;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
template<typename T>
|
||||
class vector4_base
|
||||
{
|
||||
public:
|
||||
union { T x,r; };
|
||||
union { T y,g; };
|
||||
union { T z,b; };
|
||||
union { T w,a; };
|
||||
|
||||
vector4_base() {}
|
||||
vector4_base(float nx, float ny, float nz, float nw)
|
||||
{
|
||||
x = nx;
|
||||
y = ny;
|
||||
z = nz;
|
||||
w = nw;
|
||||
}
|
||||
|
||||
vector4_base operator +(const vector4_base &v) const { return vector4_base(x+v.x, y+v.y, z+v.z, w+v.w); }
|
||||
vector4_base operator -(const vector4_base &v) const { return vector4_base(x-v.x, y-v.y, z-v.z, w-v.w); }
|
||||
vector4_base operator -() const { return vector4_base(-x, -y, -z, -w); }
|
||||
vector4_base operator *(const vector4_base &v) const { return vector4_base(x*v.x, y*v.y, z*v.z, w*v.w); }
|
||||
vector4_base operator *(const T v) const { return vector4_base(x*v, y*v, z*v, w*v); }
|
||||
|
||||
const vector4_base &operator =(const vector4_base &v) { x = v.x; y = v.y; z = v.z; w = v.w; return *this; }
|
||||
|
||||
const vector4_base &operator +=(const vector4_base &v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }
|
||||
const vector4_base &operator -=(const vector4_base &v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }
|
||||
const vector4_base &operator *=(const T v) { x *= v; y *= v; z *= v; w *= v; return *this; }
|
||||
|
||||
bool operator ==(const vector4_base &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } //TODO: do this with an eps instead
|
||||
|
||||
operator const T* () { return &x; }
|
||||
};
|
||||
|
||||
typedef vector4_base<float> vec4;
|
||||
typedef vector4_base<bool> bvec4;
|
||||
typedef vector4_base<int> ivec4;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,199 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_engine.h>
|
||||
#include "ec_font.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short x, y;
|
||||
short width, height;
|
||||
short x_offset, y_offset;
|
||||
short x_advance;
|
||||
} FONT_CHARACTER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short size;
|
||||
short width, height;
|
||||
|
||||
short line_height;
|
||||
short base;
|
||||
|
||||
FONT_CHARACTER characters[256];
|
||||
|
||||
short kerning[256*256];
|
||||
} FONT_DATA;
|
||||
|
||||
int font_load(FONT *font, const char *filename)
|
||||
{
|
||||
FONT_DATA font_data;
|
||||
IOHANDLE file;
|
||||
|
||||
file = engine_openfile(filename, IOFLAG_READ);
|
||||
|
||||
if(file)
|
||||
{
|
||||
int i;
|
||||
|
||||
io_read(file, &font_data, sizeof(FONT_DATA));
|
||||
io_close(file);
|
||||
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
swap_endian(&font_data, 2, sizeof(FONT_DATA)/2);
|
||||
#endif
|
||||
|
||||
{
|
||||
float scale_factor_x = 1.0f/font_data.size;
|
||||
float scale_factor_y = 1.0f/font_data.size;
|
||||
float scale_factor_tex_x = 1.0f/font_data.width;
|
||||
float scale_factor_tex_y = 1.0f/font_data.height;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
float tex_x0 = font_data.characters[i].x*scale_factor_tex_x;
|
||||
float tex_y0 = font_data.characters[i].y*scale_factor_tex_y;
|
||||
float tex_x1 = (font_data.characters[i].x+font_data.characters[i].width)*scale_factor_tex_x;
|
||||
float tex_y1 = (font_data.characters[i].y+font_data.characters[i].height)*scale_factor_tex_y;
|
||||
|
||||
float width = font_data.characters[i].width*scale_factor_x;
|
||||
float height = font_data.characters[i].height*scale_factor_y;
|
||||
float x_offset = font_data.characters[i].x_offset*scale_factor_x;
|
||||
float y_offset = font_data.characters[i].y_offset*scale_factor_y;
|
||||
float x_advance = (font_data.characters[i].x_advance)*scale_factor_x;
|
||||
|
||||
font->characters[i].tex_x0 = tex_x0;
|
||||
font->characters[i].tex_y0 = tex_y0;
|
||||
font->characters[i].tex_x1 = tex_x1;
|
||||
font->characters[i].tex_y1 = tex_y1;
|
||||
font->characters[i].width = width;
|
||||
font->characters[i].height = height;
|
||||
font->characters[i].x_offset = x_offset;
|
||||
font->characters[i].y_offset = y_offset;
|
||||
font->characters[i].x_advance = x_advance;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < 256*256; i++)
|
||||
{
|
||||
font->kerning[i] = (char)font_data.kerning[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*int gfx_load_texture(const char *filename, int store_format, int flags);
|
||||
#define IMG_ALPHA 2*/
|
||||
|
||||
int font_set_load(FONT_SET *font_set, const char *font_filename, const char *text_texture_filename, const char *outline_texture_filename, int fonts, ...)
|
||||
{
|
||||
int i;
|
||||
va_list va;
|
||||
|
||||
font_set->font_count = fonts;
|
||||
|
||||
va_start(va, fonts);
|
||||
for (i = 0; i < fonts; i++)
|
||||
{
|
||||
int size;
|
||||
char composed_font_filename[256];
|
||||
char composed_text_texture_filename[256];
|
||||
char composed_outline_texture_filename[256];
|
||||
FONT *font = &font_set->fonts[i];
|
||||
|
||||
size = va_arg(va, int);
|
||||
str_format(composed_font_filename, sizeof(composed_font_filename), font_filename, size);
|
||||
str_format(composed_text_texture_filename, sizeof(composed_text_texture_filename), text_texture_filename, size);
|
||||
str_format(composed_outline_texture_filename, sizeof(composed_outline_texture_filename), outline_texture_filename, size);
|
||||
|
||||
if (font_load(font, composed_font_filename))
|
||||
{
|
||||
dbg_msg("font/loading", "failed loading font %s.", composed_font_filename);
|
||||
va_end(va);
|
||||
return -1;
|
||||
}
|
||||
|
||||
font->size = size;
|
||||
font->text_texture = gfx_load_texture(composed_text_texture_filename, IMG_ALPHA, TEXLOAD_NORESAMPLE);
|
||||
font->outline_texture = gfx_load_texture(composed_outline_texture_filename, IMG_ALPHA, TEXLOAD_NORESAMPLE);
|
||||
}
|
||||
|
||||
va_end(va);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float font_text_width(FONT *font, const char *text, float size, int length)
|
||||
{
|
||||
float width = 0.0f;
|
||||
|
||||
const unsigned char *c = (unsigned char *)text;
|
||||
int chars_left;
|
||||
if (length == -1)
|
||||
chars_left = strlen(text);
|
||||
else
|
||||
chars_left = length;
|
||||
|
||||
while (chars_left--)
|
||||
{
|
||||
float tex_x0, tex_y0, tex_x1, tex_y1;
|
||||
float char_width, char_height;
|
||||
float x_offset, y_offset, x_advance;
|
||||
float advance;
|
||||
|
||||
font_character_info(font, *c, &tex_x0, &tex_y0, &tex_x1, &tex_y1, &char_width, &char_height, &x_offset, &y_offset, &x_advance);
|
||||
|
||||
advance = x_advance + font_kerning(font, *c, *(c+1));
|
||||
|
||||
width += advance;
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
return width*size;
|
||||
}
|
||||
|
||||
void font_character_info(FONT *font, unsigned char c, float *tex_x0, float *tex_y0, float *tex_x1, float *tex_y1, float *width, float *height, float *x_offset, float *y_offset, float *x_advance)
|
||||
{
|
||||
CHARACTER *character = &font->characters[c];
|
||||
|
||||
*tex_x0 = character->tex_x0;
|
||||
*tex_y0 = character->tex_y0;
|
||||
*tex_x1 = character->tex_x1;
|
||||
*tex_y1 = character->tex_y1;
|
||||
*width = character->width;
|
||||
*height = character->height;
|
||||
*x_offset = character->x_offset;
|
||||
*y_offset = character->y_offset;
|
||||
*x_advance = character->x_advance;
|
||||
}
|
||||
|
||||
float font_kerning(FONT *font, unsigned char c1, unsigned char c2)
|
||||
{
|
||||
return font->kerning[c1 + c2*256] / (float)font->size;
|
||||
}
|
||||
|
||||
FONT *font_set_pick(FONT_SET *font_set, float size)
|
||||
{
|
||||
int i;
|
||||
FONT *picked_font = 0x0;
|
||||
|
||||
for (i = font_set->font_count-1; i >= 0; i--)
|
||||
{
|
||||
FONT *font = &font_set->fonts[i];
|
||||
|
||||
if (font->size >= size)
|
||||
picked_font = font;
|
||||
}
|
||||
|
||||
if (!picked_font)
|
||||
picked_font = &font_set->fonts[font_set->font_count-1];
|
||||
|
||||
return picked_font;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef _FONT_H
|
||||
#define _FONT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float tex_x0;
|
||||
float tex_y0;
|
||||
float tex_x1;
|
||||
float tex_y1;
|
||||
float width;
|
||||
float height;
|
||||
float x_offset;
|
||||
float y_offset;
|
||||
float x_advance;
|
||||
}
|
||||
CHARACTER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int text_texture;
|
||||
int outline_texture;
|
||||
int size;
|
||||
CHARACTER characters[256];
|
||||
char kerning[256*256];
|
||||
} FONT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int font_count;
|
||||
FONT fonts[14];
|
||||
} FONT_SET;
|
||||
|
||||
int font_load(FONT *font, const char *filename);
|
||||
int font_set_load(FONT_SET *font_set, const char *font_filename, const char *text_texture_filename, const char *outline_texture_filename, int fonts, ...);
|
||||
float font_text_width(FONT *font, const char *text, float size, int width);
|
||||
void font_character_info(FONT *font, unsigned char c, float *tex_x0, float *tex_y0, float *tex_x1, float *tex_y1, float *width, float *height, float *x_offset, float *y_offset, float *x_advance);
|
||||
float font_kerning(FONT *font, unsigned char c1, unsigned char c2);
|
||||
FONT *font_set_pick(FONT_SET *font_set, float size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,239 +0,0 @@
|
||||
#include <base/system.h>
|
||||
#include <string.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
static int word_length(const char *text)
|
||||
{
|
||||
int s = 1;
|
||||
while(1)
|
||||
{
|
||||
if(*text == 0)
|
||||
return s-1;
|
||||
if(*text == '\n' || *text == '\t' || *text == ' ')
|
||||
return s;
|
||||
text++;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
static float text_r=1;
|
||||
static float text_g=1;
|
||||
static float text_b=1;
|
||||
static float text_a=1;
|
||||
|
||||
static FONT_SET *default_font_set = 0;
|
||||
void gfx_text_set_default_font(void *font)
|
||||
{
|
||||
default_font_set = (FONT_SET *)font;
|
||||
}
|
||||
|
||||
|
||||
void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags)
|
||||
{
|
||||
mem_zero(cursor, sizeof(*cursor));
|
||||
cursor->font_size = font_size;
|
||||
cursor->start_x = x;
|
||||
cursor->start_y = y;
|
||||
cursor->x = x;
|
||||
cursor->y = y;
|
||||
cursor->line_count = 1;
|
||||
cursor->line_width = -1;
|
||||
cursor->flags = flags;
|
||||
cursor->charcount = 0;
|
||||
}
|
||||
|
||||
void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
|
||||
{
|
||||
FONT_SET *font_set = (FONT_SET *)cursor->font_set;
|
||||
float screen_x0, screen_y0, screen_x1, screen_y1;
|
||||
float fake_to_screen_x, fake_to_screen_y;
|
||||
int actual_x, actual_y;
|
||||
|
||||
FONT *font;
|
||||
int actual_size;
|
||||
int i;
|
||||
int got_new_line = 0;
|
||||
float draw_x, draw_y;
|
||||
float cursor_x, cursor_y;
|
||||
const char *end;
|
||||
|
||||
float size = cursor->font_size;
|
||||
|
||||
/* to correct coords, convert to screen coords, round, and convert back */
|
||||
gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
|
||||
|
||||
fake_to_screen_x = (gfx_screenwidth()/(screen_x1-screen_x0));
|
||||
fake_to_screen_y = (gfx_screenheight()/(screen_y1-screen_y0));
|
||||
actual_x = cursor->x * fake_to_screen_x;
|
||||
actual_y = cursor->y * fake_to_screen_y;
|
||||
|
||||
cursor_x = actual_x / fake_to_screen_x;
|
||||
cursor_y = actual_y / fake_to_screen_y;
|
||||
|
||||
/* same with size */
|
||||
actual_size = size * fake_to_screen_y;
|
||||
size = actual_size / fake_to_screen_y;
|
||||
|
||||
if(!font_set)
|
||||
font_set = default_font_set;
|
||||
|
||||
font = font_set_pick(font_set, actual_size);
|
||||
|
||||
if (length < 0)
|
||||
length = strlen(text);
|
||||
|
||||
end = text + length;
|
||||
|
||||
/* if we don't want to render, we can just skip the first outline pass */
|
||||
i = 1;
|
||||
if(cursor->flags&TEXTFLAG_RENDER)
|
||||
i = 0;
|
||||
|
||||
for(;i < 2; i++)
|
||||
{
|
||||
const unsigned char *current = (unsigned char *)text;
|
||||
int to_render = length;
|
||||
draw_x = cursor_x;
|
||||
draw_y = cursor_y;
|
||||
|
||||
if(cursor->flags&TEXTFLAG_RENDER)
|
||||
{
|
||||
if (i == 0)
|
||||
gfx_texture_set(font->outline_texture);
|
||||
else
|
||||
gfx_texture_set(font->text_texture);
|
||||
|
||||
gfx_quads_begin();
|
||||
if (i == 0)
|
||||
gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a);
|
||||
else
|
||||
gfx_setcolor(text_r, text_g, text_b, text_a);
|
||||
}
|
||||
|
||||
while(to_render > 0)
|
||||
{
|
||||
int new_line = 0;
|
||||
int this_batch = to_render;
|
||||
if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END))
|
||||
{
|
||||
int wlen = word_length((char *)current);
|
||||
TEXT_CURSOR compare = *cursor;
|
||||
compare.x = draw_x;
|
||||
compare.y = draw_y;
|
||||
compare.flags &= ~TEXTFLAG_RENDER;
|
||||
compare.line_width = -1;
|
||||
gfx_text_ex(&compare, text, wlen);
|
||||
|
||||
if(compare.x-draw_x > cursor->line_width)
|
||||
{
|
||||
/* word can't be fitted in one line, cut it */
|
||||
TEXT_CURSOR cutter = *cursor;
|
||||
cutter.charcount = 0;
|
||||
cutter.x = draw_x;
|
||||
cutter.y = draw_y;
|
||||
cutter.flags &= ~TEXTFLAG_RENDER;
|
||||
cutter.flags |= TEXTFLAG_STOP_AT_END;
|
||||
|
||||
gfx_text_ex(&cutter, (const char *)current, wlen);
|
||||
wlen = cutter.charcount;
|
||||
new_line = 1;
|
||||
|
||||
if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */
|
||||
wlen = 0;
|
||||
}
|
||||
else if(compare.x-cursor->start_x > cursor->line_width)
|
||||
{
|
||||
new_line = 1;
|
||||
wlen = 0;
|
||||
}
|
||||
|
||||
this_batch = wlen;
|
||||
}
|
||||
|
||||
if((const char *)current+this_batch > end)
|
||||
this_batch = (const char *)end-(const char *)current;
|
||||
|
||||
to_render -= this_batch;
|
||||
|
||||
while(this_batch-- > 0)
|
||||
{
|
||||
float tex_x0, tex_y0, tex_x1, tex_y1;
|
||||
float width, height;
|
||||
float x_offset, y_offset, x_advance;
|
||||
float advance;
|
||||
|
||||
if(*current == '\n')
|
||||
{
|
||||
draw_x = cursor->start_x;
|
||||
draw_y += size;
|
||||
draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
|
||||
draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;
|
||||
current++;
|
||||
continue;
|
||||
}
|
||||
|
||||
font_character_info(font, *current, &tex_x0, &tex_y0, &tex_x1, &tex_y1, &width, &height, &x_offset, &y_offset, &x_advance);
|
||||
|
||||
if(cursor->flags&TEXTFLAG_RENDER)
|
||||
{
|
||||
gfx_quads_setsubset(tex_x0, tex_y0, tex_x1, tex_y1);
|
||||
gfx_quads_drawTL(draw_x+x_offset*size, draw_y+y_offset*size, width*size, height*size);
|
||||
}
|
||||
|
||||
advance = x_advance + font_kerning(font, *current, *(current+1));
|
||||
|
||||
if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width)
|
||||
{
|
||||
/* we hit the end of the line, no more to render or count */
|
||||
to_render = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
draw_x += advance*size;
|
||||
cursor->charcount++;
|
||||
current++;
|
||||
}
|
||||
|
||||
if(new_line)
|
||||
{
|
||||
draw_x = cursor->start_x;
|
||||
draw_y += size;
|
||||
got_new_line = 1;
|
||||
draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
|
||||
draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;
|
||||
}
|
||||
}
|
||||
|
||||
if(cursor->flags&TEXTFLAG_RENDER)
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
cursor->x = draw_x;
|
||||
|
||||
if(got_new_line)
|
||||
cursor->y = draw_y;
|
||||
}
|
||||
|
||||
void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width)
|
||||
{
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER);
|
||||
cursor.line_width = max_width;
|
||||
gfx_text_ex(&cursor, text, -1);
|
||||
}
|
||||
|
||||
float gfx_text_width(void *font_set_v, float size, const char *text, int length)
|
||||
{
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, 0, 0, size, 0);
|
||||
gfx_text_ex(&cursor, text, length);
|
||||
return cursor.x;
|
||||
}
|
||||
|
||||
void gfx_text_color(float r, float g, float b, float a)
|
||||
{
|
||||
text_r = r;
|
||||
text_g = g;
|
||||
text_b = b;
|
||||
text_a = a;
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <string.h>
|
||||
#include "SDL.h"
|
||||
|
||||
#include <base/system.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
static struct
|
||||
{
|
||||
unsigned char presses;
|
||||
unsigned char releases;
|
||||
} input_count[2][1024] = {{{0}}, {{0}}};
|
||||
|
||||
static unsigned char input_state[2][1024] = {{0}, {0}};
|
||||
|
||||
static int input_current = 0;
|
||||
static int input_grabbed = 0;
|
||||
|
||||
static unsigned int last_release = 0;
|
||||
static unsigned int release_delta = -1;
|
||||
|
||||
extern SDL_Joystick * EC_joystick;
|
||||
extern SDL_Surface *EC_screen_surface;
|
||||
|
||||
static void add_event(char c, int key, int flags);
|
||||
|
||||
void inp_mouse_relative(int *x, int *y)
|
||||
{
|
||||
int nx = 0, ny = 0;
|
||||
float sens = config.inp_mousesens/100.0f;
|
||||
|
||||
if(config.inp_grab)
|
||||
SDL_GetRelativeMouseState(&nx, &ny);
|
||||
else
|
||||
{
|
||||
if(input_grabbed)
|
||||
{
|
||||
SDL_GetMouseState(&nx,&ny);
|
||||
SDL_WarpMouse(gfx_screenwidth()/2,gfx_screenheight()/2);
|
||||
nx -= gfx_screenwidth()/2; ny -= gfx_screenheight()/2;
|
||||
}
|
||||
}
|
||||
|
||||
*x = nx*sens;
|
||||
*y = ny*sens;
|
||||
}
|
||||
|
||||
void inp_mouse_absolute(int *x, int *y)
|
||||
{
|
||||
SDL_GetMouseState(x, y);
|
||||
}
|
||||
|
||||
void inp_warp_mouse(int x, int y)
|
||||
{
|
||||
SDL_WarpMouse(x,y);
|
||||
}
|
||||
|
||||
int inp_process_joystick()
|
||||
{
|
||||
if( !EC_joystick || !EC_screen_surface )
|
||||
return 0;
|
||||
int jx = SDL_JoystickGetAxis(EC_joystick, 0);
|
||||
int jy = SDL_JoystickGetAxis(EC_joystick, 1);
|
||||
if( abs(jx) > 10 && abs(jy) > 10 )
|
||||
{
|
||||
jx = (jx + 32768) * EC_screen_surface->w / 65536;
|
||||
jy = (jy + 32768) * EC_screen_surface->h / 65536;
|
||||
SDL_WarpMouse(jx,jy);
|
||||
SDL_PumpEvents();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
INPUT_BUFFER_SIZE=32
|
||||
};
|
||||
|
||||
static INPUT_EVENT input_events[INPUT_BUFFER_SIZE];
|
||||
static int num_events = 0;
|
||||
|
||||
static void add_event(char c, int key, int flags)
|
||||
{
|
||||
if(num_events != INPUT_BUFFER_SIZE)
|
||||
{
|
||||
input_events[num_events].ch = c;
|
||||
input_events[num_events].key = key;
|
||||
input_events[num_events].flags = flags;
|
||||
num_events++;
|
||||
}
|
||||
}
|
||||
|
||||
int inp_num_events()
|
||||
{
|
||||
return num_events;
|
||||
}
|
||||
|
||||
void inp_clear_events()
|
||||
{
|
||||
num_events = 0;
|
||||
}
|
||||
|
||||
INPUT_EVENT inp_get_event(int index)
|
||||
{
|
||||
if(index < 0 || index >= num_events)
|
||||
{
|
||||
INPUT_EVENT e = {0,0};
|
||||
return e;
|
||||
}
|
||||
|
||||
return input_events[index];
|
||||
}
|
||||
|
||||
void inp_init()
|
||||
{
|
||||
SDL_EnableUNICODE(1);
|
||||
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
||||
}
|
||||
|
||||
void inp_mouse_mode_absolute()
|
||||
{
|
||||
SDL_ShowCursor(1);
|
||||
input_grabbed = 0;
|
||||
if(config.inp_grab)
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
}
|
||||
|
||||
void inp_mouse_mode_relative()
|
||||
{
|
||||
SDL_ShowCursor(0);
|
||||
input_grabbed = 1;
|
||||
if(config.inp_grab)
|
||||
SDL_WM_GrabInput(SDL_GRAB_ON);
|
||||
}
|
||||
|
||||
int inp_mouse_doubleclick()
|
||||
{
|
||||
return release_delta < (time_freq() >> 2);
|
||||
}
|
||||
|
||||
void inp_clear_key_states()
|
||||
{
|
||||
mem_zero(input_state, sizeof(input_state));
|
||||
mem_zero(input_count, sizeof(input_count));
|
||||
}
|
||||
|
||||
int inp_key_presses(int key)
|
||||
{
|
||||
return input_count[input_current][key].presses;
|
||||
}
|
||||
|
||||
int inp_key_releases(int key)
|
||||
{
|
||||
return input_count[input_current][key].releases;
|
||||
}
|
||||
|
||||
int inp_key_state(int key)
|
||||
{
|
||||
return input_state[input_current][key];
|
||||
}
|
||||
|
||||
int inp_key_pressed(int key) { return input_state[input_current][key]; }
|
||||
int inp_key_was_pressed(int key) { return input_state[input_current^1][key]; }
|
||||
int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); }
|
||||
int inp_button_pressed(int button) { return input_state[input_current][button]; }
|
||||
|
||||
static int oldjoy = 0;
|
||||
|
||||
void inp_update()
|
||||
{
|
||||
int i;
|
||||
|
||||
if(input_grabbed && !gfx_window_active())
|
||||
inp_mouse_mode_absolute();
|
||||
|
||||
/*if(!input_grabbed && gfx_window_active())
|
||||
inp_mouse_mode_relative();*/
|
||||
|
||||
/* clear and begin count on the other one */
|
||||
input_current^=1;
|
||||
mem_zero(&input_count[input_current], sizeof(input_count[input_current]));
|
||||
mem_zero(&input_state[input_current], sizeof(input_state[input_current]));
|
||||
|
||||
{
|
||||
Uint8 *state = SDL_GetKeyState(&i);
|
||||
if(i >= KEY_LAST)
|
||||
i = KEY_LAST-1;
|
||||
mem_copy(input_state[input_current], state, i);
|
||||
}
|
||||
|
||||
/* these states must always be updated manually because they are not in the GetKeyState from SDL */
|
||||
i = SDL_GetMouseState(NULL, NULL);
|
||||
int joy = inp_process_joystick();
|
||||
if(i&SDL_BUTTON(1)) input_state[input_current][KEY_MOUSE_1] = 1; /* 1 is left */
|
||||
if(i&SDL_BUTTON(3)) input_state[input_current][KEY_MOUSE_2] = 1; /* 3 is right */
|
||||
if(i&SDL_BUTTON(2)) input_state[input_current][KEY_MOUSE_3] = 1; /* 2 is middle */
|
||||
if(i&SDL_BUTTON(4)) input_state[input_current][KEY_MOUSE_4] = 1;
|
||||
if(i&SDL_BUTTON(5)) input_state[input_current][KEY_MOUSE_5] = 1;
|
||||
if(i&SDL_BUTTON(6)) input_state[input_current][KEY_MOUSE_6] = 1;
|
||||
if(i&SDL_BUTTON(7)) input_state[input_current][KEY_MOUSE_7] = 1;
|
||||
if(i&SDL_BUTTON(8)) input_state[input_current][KEY_MOUSE_8] = 1;
|
||||
|
||||
{
|
||||
SDL_Event event;
|
||||
|
||||
while(SDL_PollEvent(&event))
|
||||
{
|
||||
int key = -1;
|
||||
int action = INPFLAG_PRESS;
|
||||
switch (event.type)
|
||||
{
|
||||
/* handle keys */
|
||||
case SDL_KEYDOWN:
|
||||
if(event.key.keysym.unicode < 255)
|
||||
add_event(event.key.keysym.unicode, 0, 0);
|
||||
key = event.key.keysym.sym;
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
action = INPFLAG_RELEASE;
|
||||
key = event.key.keysym.sym;
|
||||
break;
|
||||
|
||||
/* handle mouse buttons */
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
action = INPFLAG_RELEASE;
|
||||
|
||||
if(event.button.button == 1)
|
||||
{
|
||||
release_delta = time_get() - last_release;
|
||||
last_release = time_get();
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if(event.button.button == SDL_BUTTON_LEFT) key = KEY_MOUSE_1;
|
||||
if(event.button.button == SDL_BUTTON_RIGHT) key = KEY_MOUSE_2;
|
||||
if(event.button.button == SDL_BUTTON_MIDDLE) key = KEY_MOUSE_3;
|
||||
if(event.button.button == SDL_BUTTON_WHEELUP) key = KEY_MOUSE_WHEEL_UP;
|
||||
if(event.button.button == SDL_BUTTON_WHEELDOWN) key = KEY_MOUSE_WHEEL_DOWN;
|
||||
if(event.button.button == 6) key = KEY_MOUSE_6;
|
||||
if(event.button.button == 7) key = KEY_MOUSE_7;
|
||||
if(event.button.button == 8) key = KEY_MOUSE_8;
|
||||
break;
|
||||
|
||||
/* other messages */
|
||||
case SDL_QUIT:
|
||||
#ifdef ANDROID
|
||||
case SDL_VIDEORESIZE:
|
||||
#endif
|
||||
/* TODO: cleaner exit */
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* */
|
||||
if(key != -1)
|
||||
{
|
||||
input_count[input_current][key].presses++;
|
||||
if(action == INPFLAG_PRESS)
|
||||
input_state[input_current][key] = 1;
|
||||
add_event(0, key, action);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(joy && !input_state[input_current][KEY_MOUSE_1])
|
||||
{
|
||||
input_count[input_current][KEY_MOUSE_1].presses++;
|
||||
add_event(0, KEY_MOUSE_1, INPFLAG_PRESS);
|
||||
}
|
||||
if(!joy && oldjoy && !(i&SDL_BUTTON(1)))
|
||||
{
|
||||
input_count[input_current][KEY_MOUSE_1].presses++;
|
||||
add_event(0, KEY_MOUSE_1, INPFLAG_RELEASE);
|
||||
}
|
||||
oldjoy = joy;
|
||||
}
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_engine.h>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include <engine/external/wavpack/wavpack.h>
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#ifdef ANDROID
|
||||
#include <strings.h>
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
NUM_SAMPLES = 512,
|
||||
NUM_VOICES = 64,
|
||||
NUM_CHANNELS = 16,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short *data;
|
||||
int num_frames;
|
||||
int rate;
|
||||
int channels;
|
||||
int loop_start;
|
||||
int loop_end;
|
||||
} SAMPLE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int vol;
|
||||
int pan;
|
||||
} CHANNEL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SAMPLE *snd;
|
||||
CHANNEL *channel;
|
||||
int tick;
|
||||
int vol; /* 0 - 255 */
|
||||
int flags;
|
||||
int x, y;
|
||||
} VOICE;
|
||||
|
||||
static SAMPLE samples[NUM_SAMPLES] = { {0} };
|
||||
static VOICE voices[NUM_VOICES] = { {0} };
|
||||
static CHANNEL channels[NUM_CHANNELS] = { {255, 0} };
|
||||
|
||||
static LOCK sound_lock = 0;
|
||||
static int sound_enabled = 0;
|
||||
|
||||
static int center_x = 0;
|
||||
static int center_y = 0;
|
||||
|
||||
static int mixing_rate = 48000;
|
||||
static volatile int sound_volume = 100;
|
||||
|
||||
static int next_voice = 0;
|
||||
static int * mix_buffer = NULL;
|
||||
|
||||
void snd_set_channel(int cid, float vol, float pan)
|
||||
{
|
||||
channels[cid].vol = (int)(vol*255.0f);
|
||||
channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */
|
||||
}
|
||||
|
||||
static int play(int cid, int sid, int flags, float x, float y)
|
||||
{
|
||||
int vid = -1;
|
||||
int i;
|
||||
|
||||
lock_wait(sound_lock);
|
||||
|
||||
/* search for voice */
|
||||
for(i = 0; i < NUM_VOICES; i++)
|
||||
{
|
||||
int id = (next_voice + i) % NUM_VOICES;
|
||||
if(!voices[id].snd)
|
||||
{
|
||||
vid = id;
|
||||
next_voice = id+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* voice found, use it */
|
||||
if(vid != -1)
|
||||
{
|
||||
voices[vid].snd = &samples[sid];
|
||||
voices[vid].channel = &channels[cid];
|
||||
voices[vid].tick = 0;
|
||||
voices[vid].vol = 255;
|
||||
voices[vid].flags = flags;
|
||||
voices[vid].x = (int)x;
|
||||
voices[vid].y = (int)y;
|
||||
}
|
||||
|
||||
lock_release(sound_lock);
|
||||
return vid;
|
||||
}
|
||||
|
||||
int snd_play_at(int cid, int sid, int flags, float x, float y)
|
||||
{
|
||||
return play(cid, sid, flags|SNDFLAG_POS, x, y);
|
||||
}
|
||||
|
||||
int snd_play(int cid, int sid, int flags)
|
||||
{
|
||||
return play(cid, sid, flags, 0, 0);
|
||||
}
|
||||
|
||||
void snd_stop(int vid)
|
||||
{
|
||||
/* TODO: a nice fade out */
|
||||
lock_wait(sound_lock);
|
||||
voices[vid].snd = 0;
|
||||
lock_release(sound_lock);
|
||||
}
|
||||
|
||||
/* TODO: there should be a faster way todo this */
|
||||
static short int2short(int i)
|
||||
{
|
||||
if(i > 0x7fff)
|
||||
return 0x7fff;
|
||||
else if(i < -0x7fff)
|
||||
return -0x7fff;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int iabs(int i)
|
||||
{
|
||||
if(i<0)
|
||||
return -i;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void mix(short *final_out, unsigned frames)
|
||||
{
|
||||
int i, s;
|
||||
int master_vol;
|
||||
|
||||
/* aquire lock while we are mixing */
|
||||
lock_wait(sound_lock);
|
||||
|
||||
#ifdef ANDROID
|
||||
bzero(mix_buffer, frames * 2 * sizeof(int));
|
||||
#else
|
||||
memset(mix_buffer, 0, sizeof(mix_buffer));
|
||||
#endif
|
||||
|
||||
master_vol = sound_volume;
|
||||
|
||||
for(i = 0; i < NUM_VOICES; i++)
|
||||
{
|
||||
if(voices[i].snd)
|
||||
{
|
||||
/* mix voice */
|
||||
VOICE *v = &voices[i];
|
||||
int *out = mix_buffer;
|
||||
|
||||
int step = v->snd->channels; /* setup input sources */
|
||||
short *in_l = &v->snd->data[v->tick*step];
|
||||
short *in_r = &v->snd->data[v->tick*step+1];
|
||||
|
||||
int end = v->snd->num_frames-v->tick;
|
||||
|
||||
int rvol = v->channel->vol;
|
||||
int lvol = v->channel->vol;
|
||||
|
||||
/* make sure that we don't go outside the sound data */
|
||||
if(frames < end)
|
||||
end = frames;
|
||||
|
||||
/* check if we have a mono sound */
|
||||
if(v->snd->channels == 1)
|
||||
in_r = in_l;
|
||||
|
||||
/* volume calculation */
|
||||
if(v->flags&SNDFLAG_POS && v->channel->pan)
|
||||
{
|
||||
/* TODO: we should respect the channel panning value */
|
||||
const int range = 1500; /* magic value, remove */
|
||||
int dx = v->x - center_x;
|
||||
int dy = v->y - center_y;
|
||||
int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */
|
||||
int p = iabs(dx);
|
||||
if(dist < range)
|
||||
{
|
||||
/* panning */
|
||||
if(dx > 0)
|
||||
lvol = ((range-p)*lvol)/range;
|
||||
else
|
||||
rvol = ((range-p)*rvol)/range;
|
||||
|
||||
/* falloff */
|
||||
lvol = (lvol*(range-dist))/range;
|
||||
rvol = (rvol*(range-dist))/range;
|
||||
}
|
||||
else
|
||||
{
|
||||
lvol = 0;
|
||||
rvol = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process all frames */
|
||||
for(s = 0; s < end; s++)
|
||||
{
|
||||
*out++ += (*in_l)*lvol;
|
||||
*out++ += (*in_r)*rvol;
|
||||
in_l += step;
|
||||
in_r += step;
|
||||
v->tick++;
|
||||
}
|
||||
|
||||
/* free voice if not used any more */
|
||||
if(v->tick == v->snd->num_frames)
|
||||
v->snd = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* release the lock */
|
||||
lock_release(sound_lock);
|
||||
|
||||
{
|
||||
/* clamp accumulated values */
|
||||
/* TODO: this seams slow */
|
||||
for(i = 0; i < frames; i++)
|
||||
{
|
||||
int j = i<<1;
|
||||
int vl = ((mix_buffer[j]*master_vol)/101)>>8;
|
||||
int vr = ((mix_buffer[j+1]*master_vol)/101)>>8;
|
||||
|
||||
final_out[j] = int2short(vl);
|
||||
final_out[j+1] = int2short(vr);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
swap_endian(final_out, sizeof(short), frames * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sdlcallback(void *unused, Uint8 *stream, int len)
|
||||
{
|
||||
mix((short *)stream, len/2/2);
|
||||
}
|
||||
|
||||
int snd_init()
|
||||
{
|
||||
SDL_AudioSpec format, format2;
|
||||
|
||||
sound_lock = lock_create();
|
||||
|
||||
if(!config.snd_enable)
|
||||
return 0;
|
||||
|
||||
mixing_rate = config.snd_rate;
|
||||
|
||||
/* Set 16-bit stereo audio at 22Khz */
|
||||
format.freq = config.snd_rate;
|
||||
format.format = AUDIO_S16;
|
||||
format.channels = 2;
|
||||
format.samples = config.snd_buffer_size;
|
||||
format.callback = sdlcallback;
|
||||
format.userdata = NULL;
|
||||
|
||||
/* Open the audio device and start playing sound! */
|
||||
if(SDL_OpenAudio(&format, &format2) < 0)
|
||||
{
|
||||
dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
dbg_msg("client/sound", "sound init successful");
|
||||
|
||||
mix_buffer = (int *)malloc(format2.samples * 2 * sizeof(int));
|
||||
|
||||
SDL_PauseAudio(0);
|
||||
|
||||
sound_enabled = 1;
|
||||
snd_update(); /* update the volume */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_update()
|
||||
{
|
||||
/* update volume */
|
||||
int wanted_volume = config.snd_volume;
|
||||
|
||||
if(!gfx_window_active() && config.snd_nonactive_mute)
|
||||
wanted_volume = 0;
|
||||
|
||||
if(wanted_volume != sound_volume)
|
||||
{
|
||||
lock_wait(sound_lock);
|
||||
sound_volume = wanted_volume;
|
||||
lock_release(sound_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_shutdown()
|
||||
{
|
||||
SDL_CloseAudio();
|
||||
lock_destroy(sound_lock);
|
||||
if( mix_buffer )
|
||||
free(mix_buffer);
|
||||
mix_buffer = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_alloc_id()
|
||||
{
|
||||
/* TODO: linear search, get rid of it */
|
||||
unsigned sid;
|
||||
for(sid = 0; sid < NUM_SAMPLES; sid++)
|
||||
{
|
||||
if(samples[sid].data == 0x0)
|
||||
return sid;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rate_convert(int sid)
|
||||
{
|
||||
SAMPLE *snd = &samples[sid];
|
||||
int num_frames = 0;
|
||||
short *new_data = 0;
|
||||
int i;
|
||||
|
||||
/* make sure that we need to convert this sound */
|
||||
if(!snd->data || snd->rate == mixing_rate)
|
||||
return;
|
||||
|
||||
/* allocate new data */
|
||||
num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate);
|
||||
new_data = (Sint16 *)mem_alloc(num_frames*snd->channels*sizeof(short), 1);
|
||||
|
||||
for(i = 0; i < num_frames; i++)
|
||||
{
|
||||
/* resample TODO: this should be done better, like linear atleast */
|
||||
float a = i/(float)num_frames;
|
||||
int f = (int)(a*snd->num_frames);
|
||||
if(f >= snd->num_frames)
|
||||
f = snd->num_frames-1;
|
||||
|
||||
/* set new data */
|
||||
if(snd->channels == 1)
|
||||
new_data[i] = snd->data[f];
|
||||
else if(snd->channels == 2)
|
||||
{
|
||||
new_data[i*2] = snd->data[f*2];
|
||||
new_data[i*2+1] = snd->data[f*2+1];
|
||||
}
|
||||
}
|
||||
|
||||
/* free old data and apply new */
|
||||
mem_free(snd->data);
|
||||
snd->data = new_data;
|
||||
snd->num_frames = num_frames;
|
||||
}
|
||||
|
||||
|
||||
static IOHANDLE file = NULL;
|
||||
|
||||
static int read_data(void *buffer, int size)
|
||||
{
|
||||
return io_read(file, buffer, size);
|
||||
}
|
||||
|
||||
int snd_load_wv(const char *filename)
|
||||
{
|
||||
SAMPLE *snd;
|
||||
int sid = -1;
|
||||
char error[100];
|
||||
WavpackContext *context;
|
||||
|
||||
/* don't waste memory on sound when we are stress testing */
|
||||
if(config.dbg_stress)
|
||||
return -1;
|
||||
|
||||
/* no need to load sound when we are running with no sound */
|
||||
if(!sound_enabled)
|
||||
return 1;
|
||||
|
||||
file = engine_openfile(filename, IOFLAG_READ); /* TODO: use system.h stuff for this */
|
||||
if(!file)
|
||||
{
|
||||
dbg_msg("sound/wv", "failed to open %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sid = snd_alloc_id();
|
||||
if(sid < 0)
|
||||
return -1;
|
||||
snd = &samples[sid];
|
||||
|
||||
context = WavpackOpenFileInput(read_data, error);
|
||||
if (context)
|
||||
{
|
||||
int samples = WavpackGetNumSamples(context);
|
||||
int bitspersample = WavpackGetBitsPerSample(context);
|
||||
unsigned int samplerate = WavpackGetSampleRate(context);
|
||||
int channels = WavpackGetNumChannels(context);
|
||||
int *data;
|
||||
int *src;
|
||||
short *dst;
|
||||
int i;
|
||||
|
||||
snd->channels = channels;
|
||||
snd->rate = samplerate;
|
||||
|
||||
if(snd->channels > 2)
|
||||
{
|
||||
dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
if(snd->rate != 44100)
|
||||
{
|
||||
dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
if(bitspersample != 16)
|
||||
{
|
||||
dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (int *)mem_alloc(4*samples*channels, 1);
|
||||
WavpackUnpackSamples(context, data, samples); /* TODO: check return value */
|
||||
src = data;
|
||||
|
||||
snd->data = (short *)mem_alloc(2*samples*channels, 1);
|
||||
dst = snd->data;
|
||||
|
||||
for (i = 0; i < samples*channels; i++)
|
||||
*dst++ = (short)*src++;
|
||||
|
||||
mem_free(data);
|
||||
|
||||
snd->num_frames = samples;
|
||||
snd->loop_start = -1;
|
||||
snd->loop_end = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("sound/wv", "failed to open %s: %s", filename, error);
|
||||
}
|
||||
|
||||
io_close(file);
|
||||
file = NULL;
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("sound/wv", "loaded %s", filename);
|
||||
|
||||
rate_convert(sid);
|
||||
return sid;
|
||||
}
|
||||
|
||||
void snd_set_listener_pos(float x, float y)
|
||||
{
|
||||
center_x = (int)x;
|
||||
center_y = (int)y;
|
||||
}
|
||||
@@ -1,737 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include <engine/e_network.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_memheap.h>
|
||||
#include <engine/e_engine.h>
|
||||
|
||||
#include <mastersrv/mastersrv.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern NETCLIENT *net;
|
||||
|
||||
|
||||
/* ------ server browse ---- */
|
||||
/* TODO: move all this to a separate file */
|
||||
|
||||
typedef struct SERVERENTRY_t SERVERENTRY;
|
||||
struct SERVERENTRY_t
|
||||
{
|
||||
NETADDR addr;
|
||||
int64 request_time;
|
||||
int got_info;
|
||||
SERVER_INFO info;
|
||||
|
||||
SERVERENTRY *next_ip; /* ip hashed list */
|
||||
|
||||
SERVERENTRY *prev_req; /* request list */
|
||||
SERVERENTRY *next_req;
|
||||
};
|
||||
|
||||
static HEAP *serverlist_heap = 0;
|
||||
static SERVERENTRY **serverlist = 0;
|
||||
static int *sorted_serverlist = 0;
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_FAVORITES=256
|
||||
};
|
||||
|
||||
static NETADDR favorite_servers[MAX_FAVORITES];
|
||||
static int num_favorite_servers = 0;
|
||||
|
||||
static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */
|
||||
|
||||
static SERVERENTRY *first_req_server = 0; /* request list */
|
||||
static SERVERENTRY *last_req_server = 0;
|
||||
static int num_requests = 0;
|
||||
|
||||
static int need_refresh = 0;
|
||||
|
||||
static int num_sorted_servers = 0;
|
||||
static int num_sorted_servers_capacity = 0;
|
||||
static int num_servers = 0;
|
||||
static int num_server_capacity = 0;
|
||||
|
||||
static int sorthash = 0;
|
||||
static char filterstring[64] = {0};
|
||||
static char filtergametypestring[128] = {0};
|
||||
|
||||
/* the token is to keep server refresh separated from each other */
|
||||
static int current_token = 1;
|
||||
|
||||
static int serverlist_type = 0;
|
||||
static int64 broadcast_time = 0;
|
||||
|
||||
int client_serverbrowse_lan() { return serverlist_type == BROWSETYPE_LAN; }
|
||||
int client_serverbrowse_num() { return num_servers; }
|
||||
|
||||
SERVER_INFO *client_serverbrowse_get(int index)
|
||||
{
|
||||
if(index < 0 || index >= num_servers)
|
||||
return 0;
|
||||
return &serverlist[index]->info;
|
||||
}
|
||||
|
||||
int client_serverbrowse_sorted_num() { return num_sorted_servers; }
|
||||
|
||||
SERVER_INFO *client_serverbrowse_sorted_get(int index)
|
||||
{
|
||||
if(index < 0 || index >= num_sorted_servers)
|
||||
return 0;
|
||||
return &serverlist[sorted_serverlist[index]]->info;
|
||||
}
|
||||
|
||||
|
||||
int client_serverbrowse_num_requests()
|
||||
{
|
||||
return num_requests;
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
return strcmp(a->info.name, b->info.name);
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
return strcmp(a->info.map, b->info.map);
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
if(a->info.latency > b->info.latency) return 1;
|
||||
if(a->info.latency < b->info.latency) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
return strcmp(a->info.gametype, b->info.gametype);
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
if(a->info.progression > b->info.progression) return 1;
|
||||
if(a->info.progression < b->info.progression) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi)
|
||||
{
|
||||
SERVERENTRY *a = serverlist[*(const int*)ai];
|
||||
SERVERENTRY *b = serverlist[*(const int*)bi];
|
||||
if(a->info.num_players > b->info.num_players) return 1;
|
||||
if(a->info.num_players < b->info.num_players) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void client_serverbrowse_filter()
|
||||
{
|
||||
int i = 0, p = 0;
|
||||
num_sorted_servers = 0;
|
||||
|
||||
/* allocate the sorted list */
|
||||
if(num_sorted_servers_capacity < num_servers)
|
||||
{
|
||||
if(sorted_serverlist)
|
||||
mem_free(sorted_serverlist);
|
||||
num_sorted_servers_capacity = num_servers;
|
||||
sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1);
|
||||
}
|
||||
|
||||
/* filter the servers */
|
||||
for(i = 0; i < num_servers; i++)
|
||||
{
|
||||
int filtered = 0;
|
||||
|
||||
if(config.b_filter_empty && serverlist[i]->info.num_players == 0)
|
||||
filtered = 1;
|
||||
else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players)
|
||||
filtered = 1;
|
||||
else if(config.b_filter_pw && serverlist[i]->info.flags&SRVFLAG_PASSWORD)
|
||||
filtered = 1;
|
||||
else if(config.b_filter_pure && (strcmp(serverlist[i]->info.gametype, "DM") != 0 && strcmp(serverlist[i]->info.gametype, "TDM") != 0 && strcmp(serverlist[i]->info.gametype, "CTF") != 0))
|
||||
filtered = 1;
|
||||
else if(config.b_filter_pure_map &&
|
||||
!(strcmp(serverlist[i]->info.map, "dm1") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "dm2") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "dm6") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "dm7") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "dm8") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "dm9") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "ctf1") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "ctf2") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "ctf3") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "ctf4") == 0 ||
|
||||
strcmp(serverlist[i]->info.map, "ctf5") == 0)
|
||||
)
|
||||
{
|
||||
filtered = 1;
|
||||
}
|
||||
else if(config.b_filter_ping < serverlist[i]->info.latency)
|
||||
filtered = 1;
|
||||
else if(config.b_filter_compatversion && strncmp(serverlist[i]->info.version, modc_net_version(), 3) != 0)
|
||||
filtered = 1;
|
||||
else
|
||||
{
|
||||
if(config.b_filter_string[0] != 0)
|
||||
{
|
||||
int matchfound = 0;
|
||||
|
||||
serverlist[i]->info.quicksearch_hit = 0;
|
||||
|
||||
/* match against server name */
|
||||
if(str_find_nocase(serverlist[i]->info.name, config.b_filter_string))
|
||||
{
|
||||
matchfound = 1;
|
||||
serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_SERVERNAME;
|
||||
}
|
||||
|
||||
/* match against players */
|
||||
for(p = 0; p < serverlist[i]->info.num_players; p++)
|
||||
{
|
||||
if(str_find_nocase(serverlist[i]->info.players[p].name, config.b_filter_string))
|
||||
{
|
||||
matchfound = 1;
|
||||
serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_PLAYERNAME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* match against map */
|
||||
if(str_find_nocase(serverlist[i]->info.map, config.b_filter_string))
|
||||
{
|
||||
matchfound = 1;
|
||||
serverlist[i]->info.quicksearch_hit |= BROWSEQUICK_MAPNAME;
|
||||
}
|
||||
|
||||
if(!matchfound)
|
||||
filtered = 1;
|
||||
}
|
||||
|
||||
if(!filtered && config.b_filter_gametype[0] != 0)
|
||||
{
|
||||
/* match against game type */
|
||||
if(!str_find_nocase(serverlist[i]->info.gametype, config.b_filter_gametype))
|
||||
filtered = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(filtered == 0)
|
||||
sorted_serverlist[num_sorted_servers++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
static int client_serverbrowse_sorthash()
|
||||
{
|
||||
int i = config.b_sort&0xf;
|
||||
i |= config.b_filter_empty<<4;
|
||||
i |= config.b_filter_full<<5;
|
||||
i |= config.b_filter_pw<<6;
|
||||
i |= config.b_sort_order<<7;
|
||||
i |= config.b_filter_compatversion<<8;
|
||||
i |= config.b_filter_pure<<9;
|
||||
i |= config.b_filter_pure_map<<10;
|
||||
i |= config.b_filter_ping<<16;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void client_serverbrowse_sort()
|
||||
{
|
||||
int i;
|
||||
|
||||
/* create filtered list */
|
||||
client_serverbrowse_filter();
|
||||
|
||||
/* sort */
|
||||
if(config.b_sort == BROWSESORT_NAME)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name);
|
||||
else if(config.b_sort == BROWSESORT_PING)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping);
|
||||
else if(config.b_sort == BROWSESORT_MAP)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map);
|
||||
else if(config.b_sort == BROWSESORT_NUMPLAYERS)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers);
|
||||
else if(config.b_sort == BROWSESORT_GAMETYPE)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype);
|
||||
else if(config.b_sort == BROWSESORT_PROGRESSION)
|
||||
qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression);
|
||||
|
||||
/* invert the list if requested */
|
||||
if(config.b_sort_order)
|
||||
{
|
||||
for(i = 0; i < num_sorted_servers/2; i++)
|
||||
{
|
||||
int temp = sorted_serverlist[i];
|
||||
sorted_serverlist[i] = sorted_serverlist[num_sorted_servers-i-1];
|
||||
sorted_serverlist[num_sorted_servers-i-1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* set indexes */
|
||||
for(i = 0; i < num_sorted_servers; i++)
|
||||
serverlist[sorted_serverlist[i]]->info.sorted_index = i;
|
||||
|
||||
str_copy(filtergametypestring, config.b_filter_gametype, sizeof(filtergametypestring));
|
||||
str_copy(filterstring, config.b_filter_string, sizeof(filterstring));
|
||||
sorthash = client_serverbrowse_sorthash();
|
||||
}
|
||||
|
||||
static void client_serverbrowse_remove_request(SERVERENTRY *entry)
|
||||
{
|
||||
if(entry->prev_req || entry->next_req || first_req_server == entry)
|
||||
{
|
||||
if(entry->prev_req)
|
||||
entry->prev_req->next_req = entry->next_req;
|
||||
else
|
||||
first_req_server = entry->next_req;
|
||||
|
||||
if(entry->next_req)
|
||||
entry->next_req->prev_req = entry->prev_req;
|
||||
else
|
||||
last_req_server = entry->prev_req;
|
||||
|
||||
entry->prev_req = 0;
|
||||
entry->next_req = 0;
|
||||
num_requests--;
|
||||
}
|
||||
}
|
||||
|
||||
static SERVERENTRY *client_serverbrowse_find(NETADDR *addr)
|
||||
{
|
||||
SERVERENTRY *entry = serverlist_ip[addr->ip[0]];
|
||||
|
||||
for(; entry; entry = entry->next_ip)
|
||||
{
|
||||
if(net_addr_comp(&entry->addr, addr) == 0)
|
||||
return entry;
|
||||
}
|
||||
return (SERVERENTRY*)0;
|
||||
}
|
||||
|
||||
void client_serverbrowse_queuerequest(SERVERENTRY *entry)
|
||||
{
|
||||
/* add it to the list of servers that we should request info from */
|
||||
entry->prev_req = last_req_server;
|
||||
if(last_req_server)
|
||||
last_req_server->next_req = entry;
|
||||
else
|
||||
first_req_server = entry;
|
||||
last_req_server = entry;
|
||||
|
||||
num_requests++;
|
||||
}
|
||||
|
||||
void client_serverbrowse_setinfo(SERVERENTRY *entry, SERVER_INFO *info)
|
||||
{
|
||||
int fav = entry->info.favorite;
|
||||
entry->info = *info;
|
||||
entry->info.favorite = fav;
|
||||
entry->info.netaddr = entry->addr;
|
||||
|
||||
// all these are just for nice compability
|
||||
if(entry->info.gametype[0] == '0' && entry->info.gametype[1] == 0)
|
||||
str_copy(entry->info.gametype, "DM", sizeof(entry->info.gametype));
|
||||
else if(entry->info.gametype[0] == '1' && entry->info.gametype[1] == 0)
|
||||
str_copy(entry->info.gametype, "TDM", sizeof(entry->info.gametype));
|
||||
else if(entry->info.gametype[0] == '2' && entry->info.gametype[1] == 0)
|
||||
str_copy(entry->info.gametype, "CTF", sizeof(entry->info.gametype));
|
||||
|
||||
/*if(!request)
|
||||
{
|
||||
entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
|
||||
client_serverbrowse_remove_request(entry);
|
||||
}*/
|
||||
|
||||
entry->got_info = 1;
|
||||
client_serverbrowse_sort();
|
||||
}
|
||||
|
||||
SERVERENTRY *client_serverbrowse_add(NETADDR *addr)
|
||||
{
|
||||
int hash = addr->ip[0];
|
||||
SERVERENTRY *entry = 0;
|
||||
int i;
|
||||
|
||||
/* create new entry */
|
||||
entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY));
|
||||
mem_zero(entry, sizeof(SERVERENTRY));
|
||||
|
||||
/* set the info */
|
||||
entry->addr = *addr;
|
||||
entry->info.netaddr = *addr;
|
||||
|
||||
entry->info.latency = 999;
|
||||
str_format(entry->info.address, sizeof(entry->info.address), "%d.%d.%d.%d:%d",
|
||||
addr->ip[0], addr->ip[1], addr->ip[2],
|
||||
addr->ip[3], addr->port);
|
||||
str_format(entry->info.name, sizeof(entry->info.name), "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */
|
||||
addr->ip[0], addr->ip[1], addr->ip[2],
|
||||
addr->ip[3], addr->port);
|
||||
|
||||
/*if(serverlist_type == BROWSETYPE_LAN)
|
||||
entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();*/
|
||||
|
||||
/* check if it's a favorite */
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
{
|
||||
if(net_addr_comp(addr, &favorite_servers[i]) == 0)
|
||||
entry->info.favorite = 1;
|
||||
}
|
||||
|
||||
/* add to the hash list */
|
||||
entry->next_ip = serverlist_ip[hash];
|
||||
serverlist_ip[hash] = entry;
|
||||
|
||||
if(num_servers == num_server_capacity)
|
||||
{
|
||||
SERVERENTRY **newlist;
|
||||
num_server_capacity += 100;
|
||||
newlist = (SERVERENTRY**)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1);
|
||||
mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*));
|
||||
mem_free(serverlist);
|
||||
serverlist = newlist;
|
||||
}
|
||||
|
||||
/* add to list */
|
||||
serverlist[num_servers] = entry;
|
||||
entry->info.server_index = num_servers;
|
||||
num_servers++;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info)
|
||||
{
|
||||
SERVERENTRY *entry = 0;
|
||||
if(type == BROWSESET_MASTER_ADD)
|
||||
{
|
||||
if(serverlist_type != BROWSETYPE_INTERNET)
|
||||
return;
|
||||
|
||||
if(!client_serverbrowse_find(addr))
|
||||
{
|
||||
entry = client_serverbrowse_add(addr);
|
||||
client_serverbrowse_queuerequest(entry);
|
||||
}
|
||||
}
|
||||
else if(type == BROWSESET_FAV_ADD)
|
||||
{
|
||||
if(serverlist_type != BROWSETYPE_FAVORITES)
|
||||
return;
|
||||
|
||||
if(!client_serverbrowse_find(addr))
|
||||
{
|
||||
entry = client_serverbrowse_add(addr);
|
||||
client_serverbrowse_queuerequest(entry);
|
||||
}
|
||||
}
|
||||
else if(type == BROWSESET_TOKEN)
|
||||
{
|
||||
if(token != current_token)
|
||||
return;
|
||||
|
||||
entry = client_serverbrowse_find(addr);
|
||||
if(!entry)
|
||||
entry = client_serverbrowse_add(addr);
|
||||
if(entry)
|
||||
{
|
||||
client_serverbrowse_setinfo(entry, info);
|
||||
if(serverlist_type == BROWSETYPE_LAN)
|
||||
entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();
|
||||
else
|
||||
entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
|
||||
client_serverbrowse_remove_request(entry);
|
||||
}
|
||||
}
|
||||
else if(type == BROWSESET_OLD_INTERNET)
|
||||
{
|
||||
entry = client_serverbrowse_find(addr);
|
||||
if(entry)
|
||||
{
|
||||
client_serverbrowse_setinfo(entry, info);
|
||||
|
||||
if(serverlist_type == BROWSETYPE_LAN)
|
||||
entry->info.latency = (time_get()-broadcast_time)*1000/time_freq();
|
||||
else
|
||||
entry->info.latency = (time_get()-entry->request_time)*1000/time_freq();
|
||||
client_serverbrowse_remove_request(entry);
|
||||
}
|
||||
}
|
||||
else if(type == BROWSESET_OLD_LAN)
|
||||
{
|
||||
entry = client_serverbrowse_find(addr);
|
||||
if(entry)
|
||||
if(!entry)
|
||||
entry = client_serverbrowse_add(addr);
|
||||
if(entry)
|
||||
client_serverbrowse_setinfo(entry, info);
|
||||
}
|
||||
|
||||
client_serverbrowse_sort();
|
||||
}
|
||||
|
||||
void client_serverbrowse_refresh(int type)
|
||||
{
|
||||
/* clear out everything */
|
||||
if(serverlist_heap)
|
||||
memheap_destroy(serverlist_heap);
|
||||
serverlist_heap = memheap_create();
|
||||
num_servers = 0;
|
||||
num_sorted_servers = 0;
|
||||
mem_zero(serverlist_ip, sizeof(serverlist_ip));
|
||||
first_req_server = 0;
|
||||
last_req_server = 0;
|
||||
num_requests = 0;
|
||||
|
||||
/* next token */
|
||||
current_token = (current_token+1)&0xff;
|
||||
|
||||
/* */
|
||||
serverlist_type = type;
|
||||
|
||||
if(type == BROWSETYPE_LAN)
|
||||
{
|
||||
unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];
|
||||
NETCHUNK packet;
|
||||
int i;
|
||||
|
||||
mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
||||
buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;
|
||||
|
||||
packet.client_id = -1;
|
||||
mem_zero(&packet, sizeof(packet));
|
||||
packet.address.ip[0] = 255;
|
||||
packet.address.ip[1] = 255;
|
||||
packet.address.ip[2] = 255;
|
||||
packet.address.ip[3] = 255;
|
||||
packet.flags = NETSENDFLAG_CONNLESS;
|
||||
packet.data_size = sizeof(buffer);
|
||||
packet.data = buffer;
|
||||
broadcast_time = time_get();
|
||||
|
||||
for(i = 8303; i <= 8310; i++)
|
||||
{
|
||||
packet.address.port = i;
|
||||
netclient_send(net, &packet);
|
||||
}
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("client", "broadcasting for servers");
|
||||
}
|
||||
else if(type == BROWSETYPE_INTERNET)
|
||||
need_refresh = 1;
|
||||
else if(type == BROWSETYPE_FAVORITES)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry)
|
||||
{
|
||||
/*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/
|
||||
NETCHUNK p;
|
||||
|
||||
if(config.debug)
|
||||
{
|
||||
dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
|
||||
addr->ip[0], addr->ip[1], addr->ip[2],
|
||||
addr->ip[3], addr->port);
|
||||
}
|
||||
|
||||
/*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
||||
buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/
|
||||
|
||||
p.client_id = -1;
|
||||
p.address = *addr;
|
||||
p.flags = NETSENDFLAG_CONNLESS;
|
||||
/*p.data_size = sizeof(buffer);
|
||||
p.data = buffer;
|
||||
netclient_send(net, &p);*/
|
||||
|
||||
/* send old requtest style aswell */
|
||||
p.data_size = sizeof(SERVERBROWSE_OLD_GETINFO);
|
||||
p.data = SERVERBROWSE_OLD_GETINFO;
|
||||
netclient_send(net, &p);
|
||||
|
||||
if(entry)
|
||||
entry->request_time = time_get();
|
||||
}
|
||||
|
||||
void client_serverbrowse_request(NETADDR *addr)
|
||||
{
|
||||
client_serverbrowse_request_impl(addr, 0);
|
||||
}
|
||||
|
||||
|
||||
void client_serverbrowse_update()
|
||||
{
|
||||
int64 timeout = time_freq();
|
||||
int64 now = time_get();
|
||||
int count;
|
||||
SERVERENTRY *entry, *next;
|
||||
|
||||
/* do server list requests */
|
||||
if(need_refresh && !mastersrv_refreshing())
|
||||
{
|
||||
NETADDR addr;
|
||||
NETCHUNK p;
|
||||
int i;
|
||||
|
||||
need_refresh = 0;
|
||||
|
||||
mem_zero(&p, sizeof(p));
|
||||
p.client_id = -1;
|
||||
p.flags = NETSENDFLAG_CONNLESS;
|
||||
p.data_size = sizeof(SERVERBROWSE_GETLIST);
|
||||
p.data = SERVERBROWSE_GETLIST;
|
||||
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
addr = mastersrv_get(i);
|
||||
if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
|
||||
continue;
|
||||
|
||||
p.address = addr;
|
||||
netclient_send(net, &p);
|
||||
}
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("client", "requesting server list");
|
||||
}
|
||||
|
||||
/* do timeouts */
|
||||
entry = first_req_server;
|
||||
while(1)
|
||||
{
|
||||
if(!entry) /* no more entries */
|
||||
break;
|
||||
|
||||
next = entry->next_req;
|
||||
|
||||
if(entry->request_time && entry->request_time+timeout < now)
|
||||
{
|
||||
/* timeout */
|
||||
client_serverbrowse_remove_request(entry);
|
||||
num_requests--;
|
||||
}
|
||||
|
||||
entry = next;
|
||||
}
|
||||
|
||||
/* do timeouts */
|
||||
entry = first_req_server;
|
||||
count = 0;
|
||||
while(1)
|
||||
{
|
||||
if(!entry) /* no more entries */
|
||||
break;
|
||||
|
||||
/* no more then 10 concurrent requests */
|
||||
if(count == config.b_max_requests)
|
||||
break;
|
||||
|
||||
if(entry->request_time == 0)
|
||||
client_serverbrowse_request_impl(&entry->addr, entry);
|
||||
|
||||
count++;
|
||||
entry = entry->next_req;
|
||||
}
|
||||
|
||||
/* check if we need to resort */
|
||||
/* TODO: remove the strcmp */
|
||||
if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0 || strcmp(filtergametypestring, config.b_filter_gametype) != 0)
|
||||
client_serverbrowse_sort();
|
||||
}
|
||||
|
||||
|
||||
int client_serverbrowse_isfavorite(NETADDR addr)
|
||||
{
|
||||
/* search for the address */
|
||||
int i;
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
{
|
||||
if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void client_serverbrowse_addfavorite(NETADDR addr)
|
||||
{
|
||||
int i;
|
||||
SERVERENTRY *entry;
|
||||
|
||||
if(num_favorite_servers == MAX_FAVORITES)
|
||||
return;
|
||||
|
||||
/* make sure that we don't already have the server in our list */
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
{
|
||||
if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* add the server to the list */
|
||||
favorite_servers[num_favorite_servers++] = addr;
|
||||
entry = client_serverbrowse_find(&addr);
|
||||
if(entry)
|
||||
entry->info.favorite = 1;
|
||||
dbg_msg("", "added fav, %p", entry);
|
||||
}
|
||||
|
||||
void client_serverbrowse_removefavorite(NETADDR addr)
|
||||
{
|
||||
int i;
|
||||
SERVERENTRY *entry;
|
||||
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
{
|
||||
if(net_addr_comp(&addr, &favorite_servers[i]) == 0)
|
||||
{
|
||||
mem_move(&favorite_servers[i], &favorite_servers[i+1], num_favorite_servers-(i+1));
|
||||
num_favorite_servers--;
|
||||
|
||||
entry = client_serverbrowse_find(&addr);
|
||||
if(entry)
|
||||
entry->info.favorite = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void client_serverbrowse_save()
|
||||
{
|
||||
int i;
|
||||
char addrstr[128];
|
||||
char buffer[256];
|
||||
for(i = 0; i < num_favorite_servers; i++)
|
||||
{
|
||||
net_addr_str(&favorite_servers[i], addrstr, sizeof(addrstr));
|
||||
str_format(buffer, sizeof(buffer), "add_favorite %s", addrstr);
|
||||
engine_config_write_line(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int client_serverbrowse_refreshingmasters()
|
||||
{
|
||||
return mastersrv_refreshing();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
Title: Time on the client
|
||||
|
||||
tick, intratick
|
||||
predtick, predintratick
|
||||
|
||||
prevtick tick predtick
|
||||
4 8 14
|
||||
|---------------------|---------------------|
|
||||
0 <- intratick -> 1
|
||||
0 <- ticktime(in s)-> X
|
||||
0 <- predintratick?-> 1
|
||||
@@ -1,19 +0,0 @@
|
||||
Title: Prediction
|
||||
|
||||
The engine calls <modc_predict> when reprediction is required. This happens usally when new data has arrived from the server. <modc_predict> should to prediction from the current snapshot and current snapshot tick (<client_tick> + 1) upto and including the tick returned by <client_predtick>.
|
||||
|
||||
Predicted input sent to the server can be retrived by calling <client_get_input> with the corresponding tick that you want the input for. Here is a simple example of how it might look.
|
||||
|
||||
> void modc_predict()
|
||||
> {
|
||||
> int tick;
|
||||
> prediction_reset();
|
||||
>
|
||||
> for(tick = client_tick()+1; tick <= client_predtick(); tick++)
|
||||
> {
|
||||
> MY_INPUT *input = (MY_INPUT *)client_get_input();
|
||||
> if(input)
|
||||
> prediction_apply_input(input);
|
||||
> prediction_tick();
|
||||
> }
|
||||
> }
|
||||
@@ -1,39 +0,0 @@
|
||||
Title: Server Operation
|
||||
|
||||
Section: Init
|
||||
|
||||
Section: Running
|
||||
|
||||
Here is an graph over how the server operates on each refresh.
|
||||
|
||||
(start code)
|
||||
load map
|
||||
init mod
|
||||
|
||||
while running
|
||||
if map change then
|
||||
load new map
|
||||
shutdown mod <mods_shutdown>
|
||||
reset clients to init state
|
||||
init mod <mods_init>
|
||||
end if
|
||||
|
||||
if new tick then
|
||||
call <mods_tick>
|
||||
for each client do
|
||||
create snapshot <mods_snap>
|
||||
send snapshot
|
||||
end for
|
||||
end
|
||||
|
||||
process new network messages
|
||||
end while
|
||||
|
||||
unload map
|
||||
(end)
|
||||
|
||||
|
||||
|
||||
Section: Reinit
|
||||
|
||||
Section: Shutdown
|
||||
@@ -1,58 +0,0 @@
|
||||
Title: Snapshots
|
||||
|
||||
Section: Overview
|
||||
|
||||
Topic: Definitions
|
||||
|
||||
- *Snapshot*. A is a serialized game state from which a client can render the game from. They are sent from the server at a regular interval and is created specificly for each client in order to reduce bandwidth.
|
||||
- *Delta Snapshot*. A set of data that can be applied to a snapshot in order to create a new snapshot with the updated game state.
|
||||
|
||||
Topic: Structure
|
||||
|
||||
A snapshot contains a series of items. Each item have a type, id and data.
|
||||
|
||||
- *Type*. Type of item. Could be projectile or character for example.
|
||||
- *Id*. A unique id so the client can identify the item between two snapshots.
|
||||
- *Data*. A series of 32-bit integers that contains the per item type specific data.
|
||||
|
||||
Section: Server Side
|
||||
|
||||
Topic: Creating
|
||||
|
||||
Items can be added when <mods_snap> is called using the <snap_new_item> function to insert an item to the snapshot. The server can not inspect the snapshot that is in progress of being created.
|
||||
|
||||
Section: Client Side
|
||||
|
||||
Topic: Inspection
|
||||
<modc_newsnapshot> is called when a new snapshot has arrived for processing. <snap_num_items>, <snap_get_item> and <snap_find_item> can be used to inspect the current and previous snapshot. This can be done anywhere while the client is running and not just in the <modc_newsnapshot> function. The client can also call <snap_invalidate_item> if an item contains improper information that could harm the operation of the client. This should however be done in <modc_newsnapshot> to assure that no bad data propagates into the rest of the client.
|
||||
|
||||
Topic: Rendering
|
||||
DOCTODO
|
||||
|
||||
Section: In depth
|
||||
|
||||
Topic: Compression
|
||||
|
||||
After a snapshot have been created, compression is applyed to reduce the bandwidth. There are several steps taken inorder to reduce the size of the size of the snapshot.
|
||||
|
||||
- *Delta*. The server looks in a clients backlog of snapshots to find a previous acked snapshot. It then compares the two snapshots and creates a delta snapshot containing the changes from the previous acked snapshot to the new one.
|
||||
- *Variable Integers*. The delta snapshot which is only consisting of 32-bit integers is then encoded into variable integers similar to UTF-8. Each byte has a bit that tells the decoder that it needs one more byte to decode the 32-bit integer. The first byte also contains a bit for telling the sign of the integer.
|
||||
|
||||
> ESDDDDDD EDDDDDDD EDDDDDDD EDDDDDDD
|
||||
|
||||
> E = extend
|
||||
> S = sign
|
||||
> D = data bit
|
||||
|
||||
- *Huffman*. The last step is to compress the buffer with a huffman encoder. It uses a static tree that is weighted towards 0 because it's where most of the data will be because of the delta compression.
|
||||
|
||||
Topic: Interval
|
||||
|
||||
The interval for how often a client recives a snapshot changes during the course of the connection. There are three different snapshot rates.
|
||||
|
||||
- *Init*. 5 snapshots per second. Used when a client is connecting and used until the client has acknowlaged a snapshot. This mechanism is used because the first snapshot because no delta compression can be done.
|
||||
|
||||
- *Full*. Snapshot for every tick or every even tick depending on server configuration. This is used during normal gameplay and everything is running smooth.
|
||||
|
||||
- *Recovery*. 1 snapshot each second. A client enters recovery rate when it havn't acknowlaged a snapshot over 1 second. This is to let the client to beable to recover if it has a slow connection.
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_CLIENT_INTERFACE_H
|
||||
#define ENGINE_CLIENT_INTERFACE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "e_if_other.h"
|
||||
#include "e_if_client.h"
|
||||
#include "e_if_snd.h"
|
||||
#include "e_if_gfx.h"
|
||||
#include "e_if_inp.h"
|
||||
#include "e_if_msg.h"
|
||||
#include "e_if_modc.h"
|
||||
|
||||
#include "e_console.h"
|
||||
#include "e_config.h"
|
||||
#include "client/ec_font.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_COMMON_INTERFACE_H
|
||||
#define ENGINE_COMMON_INTERFACE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "e_if_other.h"
|
||||
#include "e_if_msg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,150 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include "e_compression.h"
|
||||
|
||||
|
||||
/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */
|
||||
unsigned char *vint_pack(unsigned char *dst, int i)
|
||||
{
|
||||
*dst = (i>>25)&0x40; /* set sign bit if i<0 */
|
||||
i = i^(i>>31); /* if(i<0) i = ~i */
|
||||
|
||||
*dst |= i&0x3F; /* pack 6bit into dst */
|
||||
i >>= 6; /* discard 6 bits */
|
||||
if(i)
|
||||
{
|
||||
*dst |= 0x80; /* set extend bit */
|
||||
while(1)
|
||||
{
|
||||
dst++;
|
||||
*dst = i&(0x7F); /* pack 7bit */
|
||||
i >>= 7; /* discard 7 bits */
|
||||
*dst |= (i!=0)<<7; /* set extend bit (may branch) */
|
||||
if(!i)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dst++;
|
||||
return dst;
|
||||
}
|
||||
|
||||
const unsigned char *vint_unpack(const unsigned char *src, int *i)
|
||||
{
|
||||
int sign = (*src>>6)&1;
|
||||
*i = *src&0x3F;
|
||||
|
||||
do
|
||||
{
|
||||
if(!(*src&0x80)) break;
|
||||
src++;
|
||||
*i |= (*src&(0x7F))<<(6);
|
||||
|
||||
if(!(*src&0x80)) break;
|
||||
src++;
|
||||
*i |= (*src&(0x7F))<<(6+7);
|
||||
|
||||
if(!(*src&0x80)) break;
|
||||
src++;
|
||||
*i |= (*src&(0x7F))<<(6+7+7);
|
||||
|
||||
if(!(*src&0x80)) break;
|
||||
src++;
|
||||
*i |= (*src&(0x7F))<<(6+7+7+7);
|
||||
} while(0);
|
||||
|
||||
src++;
|
||||
*i ^= -sign; /* if(sign) *i = ~(*i) */
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
long intpack_decompress(const void *src_, int size, void *dst_)
|
||||
{
|
||||
const unsigned char *src = (unsigned char *)src_;
|
||||
const unsigned char *end = src + size;
|
||||
int *dst = (int *)dst_;
|
||||
while(src < end)
|
||||
{
|
||||
src = vint_unpack(src, dst);
|
||||
dst++;
|
||||
}
|
||||
return (long)((unsigned char *)dst-(unsigned char *)dst_);
|
||||
}
|
||||
|
||||
long intpack_compress(const void *src_, int size, void *dst_)
|
||||
{
|
||||
int *src = (int *)src_;
|
||||
unsigned char *dst = (unsigned char *)dst_;
|
||||
size /= 4;
|
||||
while(size)
|
||||
{
|
||||
dst = vint_pack(dst, *src);
|
||||
size--;
|
||||
src++;
|
||||
}
|
||||
return (long)(dst-(unsigned char *)dst_);
|
||||
}
|
||||
|
||||
|
||||
/* */
|
||||
long zerobit_compress(const void *src_, int size, void *dst_)
|
||||
{
|
||||
unsigned char *src = (unsigned char *)src_;
|
||||
unsigned char *dst = (unsigned char *)dst_;
|
||||
|
||||
while(size)
|
||||
{
|
||||
unsigned char bit = 0x80;
|
||||
unsigned char mask = 0;
|
||||
int dst_move = 1;
|
||||
int chunk = size < 8 ? size : 8;
|
||||
int b;
|
||||
size -= chunk;
|
||||
|
||||
for(b = 0; b < chunk; b++, bit>>=1)
|
||||
{
|
||||
if(*src)
|
||||
{
|
||||
dst[dst_move] = *src;
|
||||
mask |= bit;
|
||||
dst_move++;
|
||||
}
|
||||
|
||||
src++;
|
||||
}
|
||||
|
||||
*dst = mask;
|
||||
dst += dst_move;
|
||||
}
|
||||
|
||||
return (long)(dst-(unsigned char *)dst_);
|
||||
}
|
||||
|
||||
long zerobit_decompress(const void *src_, int size, void *dst_)
|
||||
{
|
||||
unsigned char *src = (unsigned char *)src_;
|
||||
unsigned char *dst = (unsigned char *)dst_;
|
||||
unsigned char *end = src + size;
|
||||
|
||||
while(src < end)
|
||||
{
|
||||
unsigned char bit = 0x80;
|
||||
unsigned char mask = *src++;
|
||||
int b;
|
||||
|
||||
for(b = 0; b < 8; b++, bit>>=1)
|
||||
{
|
||||
if(mask&bit)
|
||||
*dst++ = *src++;
|
||||
else
|
||||
*dst++ = 0;
|
||||
}
|
||||
|
||||
if(src > end)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (long)(dst-(unsigned char *)dst_);
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* variable int packing */
|
||||
unsigned char *vint_pack(unsigned char *dst, int i);
|
||||
const unsigned char *vint_unpack(const unsigned char *src, int *inout);
|
||||
long intpack_compress(const void *src, int size, void *dst);
|
||||
long intpack_decompress(const void *src, int size, void *dst);
|
||||
|
||||
/* zerobit packing */
|
||||
long zerobit_compress(const void *src, int size, void *dst);
|
||||
long zerobit_decompress(const void *src, int size, void *dst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <base/system.h>
|
||||
#include "e_if_other.h"
|
||||
#include "e_config.h"
|
||||
#include "e_linereader.h"
|
||||
#include "e_engine.h"
|
||||
|
||||
CONFIGURATION config;
|
||||
|
||||
void config_reset()
|
||||
{
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) config.name = def;
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) str_copy(config.name, def, len);
|
||||
|
||||
#include "e_config_variables.h"
|
||||
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
}
|
||||
|
||||
void config_save()
|
||||
{
|
||||
char linebuf[1024*2];
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %i", #name, config.name); engine_config_write_line(linebuf); }
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) if((flags)&CFGFLAG_SAVE){ str_format(linebuf, sizeof(linebuf), "%s %s", #name, config.name); engine_config_write_line(linebuf); }
|
||||
|
||||
#include "e_config_variables.h"
|
||||
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
}
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c) { return c->name; }
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; }
|
||||
#include "e_config_variables.h"
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; }
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str) { str_copy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; }
|
||||
#include "e_config_variables.h"
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
@@ -1,52 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef _CONFIG_H
|
||||
#define _CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name;
|
||||
#define MACRO_CONFIG_STR(name,len,def,save,desc) char name[len]; /* Flawfinder: ignore */
|
||||
#include "e_config_variables.h"
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
} CONFIGURATION;
|
||||
|
||||
extern CONFIGURATION config;
|
||||
|
||||
void config_init();
|
||||
void config_reset();
|
||||
void config_save();
|
||||
|
||||
enum
|
||||
{
|
||||
CFGFLAG_SAVE=1,
|
||||
CFGFLAG_CLIENT=2,
|
||||
CFGFLAG_SERVER=4
|
||||
};
|
||||
|
||||
typedef int (*CONFIG_INT_GETTER)(CONFIGURATION *c);
|
||||
typedef const char *(*CONFIG_STR_GETTER)(CONFIGURATION *c);
|
||||
typedef void (*CONFIG_INT_SETTER)(CONFIGURATION *c, int val);
|
||||
typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str);
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) int config_get_ ## name (CONFIGURATION *c);
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) const char *config_get_ ## name (CONFIGURATION *c);
|
||||
#include "e_config_variables.h"
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) void config_set_ ## name (CONFIGURATION *c, int val);
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) void config_set_ ## name (CONFIGURATION *c, const char *str);
|
||||
#include "e_config_variables.h"
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
/* TODO: remove this */
|
||||
#include "../game/variables.hpp"
|
||||
|
||||
|
||||
MACRO_CONFIG_STR(player_name, 32, "nameless tee", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Name of the player")
|
||||
MACRO_CONFIG_STR(clan_name, 32, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "(not used)")
|
||||
MACRO_CONFIG_STR(password, 32, "", CFGFLAG_CLIENT, "Password to the server")
|
||||
MACRO_CONFIG_STR(logfile, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filename to log all output to")
|
||||
|
||||
MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(cl_editor, 0, 0, 1, CFGFLAG_CLIENT, "")
|
||||
|
||||
MACRO_CONFIG_INT(cl_eventthread, 0, 0, 1, CFGFLAG_CLIENT, "Enables the usage of a thread to pump the events")
|
||||
|
||||
MACRO_CONFIG_INT(inp_grab, 0, 0, 1, CFGFLAG_CLIENT, "Use forceful input grabbing method")
|
||||
|
||||
MACRO_CONFIG_STR(b_filter_string, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Server browser filtering string")
|
||||
|
||||
MACRO_CONFIG_INT(b_filter_full, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out full server in browser")
|
||||
MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out empty server in browser")
|
||||
MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out password protected servers in browser")
|
||||
MACRO_CONFIG_INT(b_filter_ping, 999, 0, 999, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Ping to filter by in the server browser")
|
||||
MACRO_CONFIG_STR(b_filter_gametype, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Game types to filter")
|
||||
MACRO_CONFIG_INT(b_filter_pure, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard servers in browser")
|
||||
MACRO_CONFIG_INT(b_filter_pure_map, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-standard maps in browser")
|
||||
MACRO_CONFIG_INT(b_filter_compatversion, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Filter out non-compatible servers in browser")
|
||||
|
||||
MACRO_CONFIG_INT(b_sort, 1, 0, 256, CFGFLAG_SAVE|CFGFLAG_CLIENT, "") // Sort by ping
|
||||
MACRO_CONFIG_INT(b_sort_order, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Number of requests to use when refreshing server browser")
|
||||
|
||||
MACRO_CONFIG_INT(snd_buffer_size, 512, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound buffer size")
|
||||
#ifdef ANDROID
|
||||
MACRO_CONFIG_INT(snd_rate, 22050, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate")
|
||||
#else
|
||||
MACRO_CONFIG_INT(snd_rate, 48000, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound mixing rate")
|
||||
#endif
|
||||
MACRO_CONFIG_INT(snd_enable, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound enable")
|
||||
MACRO_CONFIG_INT(snd_volume, 100, 0, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Sound volume")
|
||||
MACRO_CONFIG_INT(snd_device, -1, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "(deprecated) Sound device to use")
|
||||
|
||||
MACRO_CONFIG_INT(snd_nonactive_mute, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
|
||||
MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution width")
|
||||
MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen resolution height")
|
||||
MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fullscreen")
|
||||
MACRO_CONFIG_INT(gfx_alphabits, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Alpha bits for framebuffer (fullscreen only)")
|
||||
MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Colors bits for framebuffer (fullscreen only)")
|
||||
MACRO_CONFIG_INT(gfx_clear, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Clear screen before rendering")
|
||||
MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Vertical sync")
|
||||
MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use texture compression")
|
||||
#ifdef ANDROID
|
||||
MACRO_CONFIG_INT(gfx_high_detail, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail")
|
||||
MACRO_CONFIG_INT(gfx_texture_quality, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples")
|
||||
#else
|
||||
MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "High detail")
|
||||
MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16, CFGFLAG_SAVE|CFGFLAG_CLIENT, "FSAA Samples")
|
||||
#endif
|
||||
MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Screen refresh rate")
|
||||
MACRO_CONFIG_INT(gfx_finish, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "")
|
||||
|
||||
MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity")
|
||||
|
||||
MACRO_CONFIG_STR(sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server name")
|
||||
MACRO_CONFIG_STR(sv_bindaddr, 128, "", CFGFLAG_SERVER, "Address to bind the server to")
|
||||
MACRO_CONFIG_INT(sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server")
|
||||
MACRO_CONFIG_INT(sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers")
|
||||
MACRO_CONFIG_STR(sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server")
|
||||
MACRO_CONFIG_INT(sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
|
||||
MACRO_CONFIG_INT(sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
|
||||
MACRO_CONFIG_INT(sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
|
||||
MACRO_CONFIG_STR(sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
|
||||
MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map")
|
||||
|
||||
MACRO_CONFIG_INT(debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode")
|
||||
MACRO_CONFIG_INT(dbg_stress, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress systems")
|
||||
MACRO_CONFIG_INT(dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Stress network")
|
||||
MACRO_CONFIG_INT(dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs")
|
||||
MACRO_CONFIG_INT(dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs")
|
||||
MACRO_CONFIG_INT(dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings")
|
||||
MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress")
|
||||
MACRO_CONFIG_INT(dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing")
|
||||
@@ -1,443 +0,0 @@
|
||||
#include <base/system.h>
|
||||
#include "e_if_other.h"
|
||||
#include "e_console.h"
|
||||
#include "e_config.h"
|
||||
#include "e_engine.h"
|
||||
#include "e_linereader.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define CONSOLE_MAX_STR_LENGTH 1024
|
||||
/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */
|
||||
#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char string_storage[CONSOLE_MAX_STR_LENGTH+1];
|
||||
char *args_start;
|
||||
|
||||
const char *command;
|
||||
const char *args[MAX_PARTS];
|
||||
unsigned int num_args;
|
||||
} PARSE_RESULT;
|
||||
|
||||
static char *str_skipblanks(char *str)
|
||||
{
|
||||
while(*str && (*str == ' ' || *str == '\t' || *str == '\n'))
|
||||
str++;
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *str_skiptoblank(char *str)
|
||||
{
|
||||
while(*str && (*str != ' ' && *str != '\t' && *str != '\n'))
|
||||
str++;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* static int digit(char c) { return '0' <= c && c <= '9'; } */
|
||||
|
||||
static int console_parse_start(PARSE_RESULT *result, const char *string, int length)
|
||||
{
|
||||
char *str;
|
||||
int len = sizeof(result->string_storage);
|
||||
if(length < len)
|
||||
len = length;
|
||||
|
||||
str_copy(result->string_storage, string, length);
|
||||
str = result->string_storage;
|
||||
|
||||
/* get command */
|
||||
str = str_skipblanks(str);
|
||||
result->command = str;
|
||||
str = str_skiptoblank(str);
|
||||
|
||||
if(*str)
|
||||
{
|
||||
str[0] = 0;
|
||||
str++;
|
||||
}
|
||||
|
||||
result->args_start = str;
|
||||
result->num_args = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int console_parse_args(PARSE_RESULT *result, const char *format)
|
||||
{
|
||||
char command;
|
||||
char *str;
|
||||
int optional = 0;
|
||||
int error = 0;
|
||||
|
||||
str = result->args_start;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* fetch command */
|
||||
command = *format;
|
||||
format++;
|
||||
|
||||
if(!command)
|
||||
break;
|
||||
|
||||
if(command == '?')
|
||||
optional = 1;
|
||||
else
|
||||
{
|
||||
str = str_skipblanks(str);
|
||||
|
||||
if(!(*str)) /* error, non optional command needs value */
|
||||
{
|
||||
if(!optional)
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* add token */
|
||||
if(*str == '"')
|
||||
{
|
||||
char *dst;
|
||||
str++;
|
||||
result->args[result->num_args++] = str;
|
||||
|
||||
dst = str; /* we might have to process escape data */
|
||||
while(1)
|
||||
{
|
||||
if(str[0] == '"')
|
||||
break;
|
||||
else if(str[0] == '\\')
|
||||
{
|
||||
if(str[1] == '\\')
|
||||
str++; /* skip due to escape */
|
||||
else if(str[1] == '"')
|
||||
str++; /* skip due to escape */
|
||||
}
|
||||
else if(str[0] == 0)
|
||||
return 1; /* return error */
|
||||
|
||||
*dst = *str;
|
||||
dst++;
|
||||
str++;
|
||||
}
|
||||
|
||||
/* write null termination */
|
||||
*dst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result->args[result->num_args++] = str;
|
||||
|
||||
if(command == 'r') /* rest of the string */
|
||||
break;
|
||||
else if(command == 'i') /* validate int */
|
||||
str = str_skiptoblank(str);
|
||||
else if(command == 'f') /* validate float */
|
||||
str = str_skiptoblank(str);
|
||||
else if(command == 's') /* validate string */
|
||||
str = str_skiptoblank(str);
|
||||
|
||||
if(str[0] != 0) /* check for end of string */
|
||||
{
|
||||
str[0] = 0;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
const char *console_arg_string(void *res, int index)
|
||||
{
|
||||
PARSE_RESULT *result = (PARSE_RESULT *)res;
|
||||
if (index < 0 || index >= result->num_args)
|
||||
return "";
|
||||
return result->args[index];
|
||||
}
|
||||
|
||||
int console_arg_int(void *res, int index)
|
||||
{
|
||||
PARSE_RESULT *result = (PARSE_RESULT *)res;
|
||||
if (index < 0 || index >= result->num_args)
|
||||
return 0;
|
||||
return atoi(result->args[index]);
|
||||
}
|
||||
|
||||
float console_arg_float(void *res, int index)
|
||||
{
|
||||
PARSE_RESULT *result = (PARSE_RESULT *)res;
|
||||
if (index < 0 || index >= result->num_args)
|
||||
return 0.0f;
|
||||
return atof(result->args[index]);
|
||||
}
|
||||
|
||||
int console_arg_num(void *result)
|
||||
{
|
||||
return ((PARSE_RESULT *)result)->num_args;
|
||||
}
|
||||
|
||||
static COMMAND *first_command = 0x0;
|
||||
|
||||
COMMAND *console_find_command(const char *name)
|
||||
{
|
||||
COMMAND *cmd;
|
||||
for (cmd = first_command; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (strcmp(cmd->name, name) == 0)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
void console_register(COMMAND *cmd)
|
||||
{
|
||||
cmd->next = first_command;
|
||||
first_command = cmd;
|
||||
}
|
||||
|
||||
static void (*print_callback)(const char *, void *) = 0x0;
|
||||
static void *print_callback_userdata;
|
||||
|
||||
void console_register_print_callback(void (*callback)(const char *, void *), void *user_data)
|
||||
{
|
||||
print_callback = callback;
|
||||
print_callback_userdata = user_data;
|
||||
}
|
||||
|
||||
void console_print(const char *str)
|
||||
{
|
||||
if (print_callback)
|
||||
print_callback(str, print_callback_userdata);
|
||||
}
|
||||
|
||||
void console_execute_line_stroked(int stroke, const char *str)
|
||||
{
|
||||
PARSE_RESULT result;
|
||||
COMMAND *command;
|
||||
|
||||
char strokestr[2] = {'0', 0};
|
||||
if(stroke)
|
||||
strokestr[0] = '1';
|
||||
|
||||
while(str)
|
||||
{
|
||||
const char *end = str;
|
||||
const char *next_part = 0;
|
||||
int in_string = 0;
|
||||
|
||||
while(*end)
|
||||
{
|
||||
if(*end == '"')
|
||||
in_string ^= 1;
|
||||
else if(*end == '\\') /* escape sequences */
|
||||
{
|
||||
if(end[1] == '"')
|
||||
end++;
|
||||
}
|
||||
else if(!in_string)
|
||||
{
|
||||
if(*end == ';') /* command separator */
|
||||
{
|
||||
next_part = end+1;
|
||||
break;
|
||||
}
|
||||
else if(*end == '#') /* comment, no need to do anything more */
|
||||
break;
|
||||
}
|
||||
|
||||
end++;
|
||||
}
|
||||
|
||||
if(console_parse_start(&result, str, (end-str) + 1) != 0)
|
||||
return;
|
||||
|
||||
command = console_find_command(result.command);
|
||||
|
||||
if(command)
|
||||
{
|
||||
int is_stroke_command = 0;
|
||||
if(result.command[0] == '+')
|
||||
{
|
||||
/* insert the stroke direction token */
|
||||
result.args[result.num_args] = strokestr;
|
||||
result.num_args++;
|
||||
is_stroke_command = 1;
|
||||
}
|
||||
|
||||
if(stroke || is_stroke_command)
|
||||
{
|
||||
if(console_parse_args(&result, command->params))
|
||||
{
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params);
|
||||
console_print(buf);
|
||||
}
|
||||
else
|
||||
command->callback(&result, command->user_data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "No such command: %s.", result.command);
|
||||
console_print(buf);
|
||||
}
|
||||
|
||||
str = next_part;
|
||||
}
|
||||
}
|
||||
|
||||
void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user)
|
||||
{
|
||||
COMMAND *cmd;
|
||||
for (cmd = first_command; cmd; cmd = cmd->next)
|
||||
{
|
||||
if(cmd->flags&flagmask)
|
||||
{
|
||||
if(str_find_nocase(cmd->name, str))
|
||||
callback(cmd->name, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
COMMAND *console_get_command(const char *str)
|
||||
{
|
||||
COMMAND *cmd;
|
||||
for (cmd = first_command; cmd; cmd = cmd->next)
|
||||
{
|
||||
if(str_comp_nocase(cmd->name, str) == 0)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
void console_execute_line(const char *str)
|
||||
{
|
||||
console_execute_line_stroked(1, str);
|
||||
}
|
||||
|
||||
static void console_execute_file_real(const char *filename)
|
||||
{
|
||||
IOHANDLE file;
|
||||
file = engine_openfile(filename, IOFLAG_READ);
|
||||
|
||||
if(file)
|
||||
{
|
||||
char *line;
|
||||
LINEREADER lr;
|
||||
|
||||
dbg_msg("console", "executing '%s'", filename);
|
||||
linereader_init(&lr, file);
|
||||
|
||||
while((line = linereader_get(&lr)))
|
||||
console_execute_line(line);
|
||||
|
||||
io_close(file);
|
||||
}
|
||||
else
|
||||
dbg_msg("console", "failed to open '%s'", filename);
|
||||
}
|
||||
|
||||
struct EXECFILE
|
||||
{
|
||||
const char *filename;
|
||||
struct EXECFILE *next;
|
||||
};
|
||||
|
||||
void console_execute_file(const char *filename)
|
||||
{
|
||||
static struct EXECFILE *first = 0;
|
||||
struct EXECFILE thisfile;
|
||||
struct EXECFILE *cur;
|
||||
struct EXECFILE *prev;
|
||||
|
||||
/* make sure that this isn't being executed already */
|
||||
for(cur = first; cur; cur = cur->next)
|
||||
if(strcmp(filename, cur->filename) == 0)
|
||||
return;
|
||||
|
||||
/* push this one to the stack */
|
||||
prev = first;
|
||||
thisfile.filename = filename;
|
||||
thisfile.next = first;
|
||||
first = &thisfile;
|
||||
|
||||
/* execute file */
|
||||
console_execute_file_real(filename);
|
||||
|
||||
/* pop this one from the stack */
|
||||
first = prev;
|
||||
}
|
||||
|
||||
static void con_echo(void *result, void *user_data)
|
||||
{
|
||||
console_print(console_arg_string(result, 0));
|
||||
}
|
||||
|
||||
static void con_exec(void *result, void *user_data)
|
||||
{
|
||||
console_execute_file(console_arg_string(result, 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CONFIG_INT_GETTER getter;
|
||||
CONFIG_INT_SETTER setter;
|
||||
} INT_VARIABLE_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CONFIG_STR_GETTER getter;
|
||||
CONFIG_STR_SETTER setter;
|
||||
} STR_VARIABLE_DATA;
|
||||
|
||||
static void int_variable_command(void *result, void *user_data)
|
||||
{
|
||||
INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data;
|
||||
|
||||
if(console_arg_num(result))
|
||||
data->setter(&config, console_arg_int(result, 0));
|
||||
else
|
||||
{
|
||||
char buf[1024];
|
||||
str_format(buf, sizeof(buf), "Value: %d", data->getter(&config));
|
||||
console_print(buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void str_variable_command(void *result, void *user_data)
|
||||
{
|
||||
STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data;
|
||||
|
||||
if(console_arg_num(result))
|
||||
data->setter(&config, console_arg_string(result, 0));
|
||||
else
|
||||
{
|
||||
char buf[1024];
|
||||
str_format(buf, sizeof(buf), "Value: %s", data->getter(&config));
|
||||
console_print(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void console_init()
|
||||
{
|
||||
MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text");
|
||||
MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file");
|
||||
|
||||
#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) }
|
||||
#define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) }
|
||||
|
||||
#include "e_config_variables.h"
|
||||
|
||||
#undef MACRO_CONFIG_INT
|
||||
#undef MACRO_CONFIG_STR
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#ifndef _CONSOLE_H
|
||||
#define _CONSOLE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data);
|
||||
|
||||
typedef struct COMMAND_t
|
||||
{
|
||||
const char *name;
|
||||
const char *params;
|
||||
int flags;
|
||||
CONSOLE_CALLBACK callback;
|
||||
void *user_data;
|
||||
const char *help;
|
||||
struct COMMAND_t *next;
|
||||
} COMMAND;
|
||||
|
||||
void console_init();
|
||||
void console_register(COMMAND *cmd);
|
||||
void console_execute_line(const char *str);
|
||||
void console_execute_line_stroked(int stroke, const char *str);
|
||||
void console_execute_file(const char *filename);
|
||||
void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user);
|
||||
COMMAND *console_get_command(const char *cmd);
|
||||
void console_print(const char *str);
|
||||
void console_register_print_callback(void (*callback)(const char *, void *user_data), void *user_data);
|
||||
|
||||
/*int console_result_string(void *result, int index, const char **str);
|
||||
int console_result_int(void *result, int index, int *i);
|
||||
int console_result_float(void *result, int index, float *f);*/
|
||||
|
||||
const char *console_arg_string(void *result, int index);
|
||||
int console_arg_int(void *result, int index);
|
||||
float console_arg_float(void *result, int index);
|
||||
int console_arg_num(void *result);
|
||||
|
||||
#define MACRO_REGISTER_COMMAND(name, params, flags, func, ptr, help) { static COMMAND cmd = { name, params, flags, func, ptr, help, 0x0}; console_register(&cmd); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,677 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include "e_datafile.h"
|
||||
#include "e_engine.h"
|
||||
#include <zlib.h>
|
||||
|
||||
static const int DEBUG=0;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
int start;
|
||||
int num;
|
||||
} DATAFILE_ITEM_TYPE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type_and_id;
|
||||
int size;
|
||||
} DATAFILE_ITEM;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char id[4];
|
||||
int version;
|
||||
int size;
|
||||
int swaplen;
|
||||
int num_item_types;
|
||||
int num_items;
|
||||
int num_raw_data;
|
||||
int item_size;
|
||||
int data_size;
|
||||
} DATAFILE_HEADER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int num_item_types;
|
||||
int num_items;
|
||||
int num_raw_data;
|
||||
int item_size;
|
||||
int data_size;
|
||||
char start[4];
|
||||
} DATAFILE_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DATAFILE_ITEM_TYPE *item_types;
|
||||
int *item_offsets;
|
||||
int *data_offsets;
|
||||
int *data_sizes;
|
||||
|
||||
char *item_start;
|
||||
char *data_start;
|
||||
} DATAFILE_INFO;
|
||||
|
||||
struct DATAFILE_t
|
||||
{
|
||||
IOHANDLE file;
|
||||
DATAFILE_INFO info;
|
||||
DATAFILE_HEADER header;
|
||||
int data_start_offset;
|
||||
char **data_ptrs;
|
||||
char *data;
|
||||
};
|
||||
|
||||
DATAFILE *datafile_load(const char *filename)
|
||||
{
|
||||
DATAFILE *df;
|
||||
IOHANDLE file;
|
||||
DATAFILE_HEADER header;
|
||||
unsigned readsize;
|
||||
|
||||
unsigned *dst;
|
||||
unsigned char *src;
|
||||
unsigned j;
|
||||
int size = 0;
|
||||
int allocsize = 0;
|
||||
|
||||
(void)dst;
|
||||
(void)src;
|
||||
(void)j;
|
||||
|
||||
|
||||
dbg_msg("datafile", "datafile loading. filename='%s'", filename);
|
||||
|
||||
file = engine_openfile(filename, IOFLAG_READ);
|
||||
if(!file)
|
||||
return 0;
|
||||
|
||||
/* TODO: change this header */
|
||||
io_read(file, &header, sizeof(header));
|
||||
if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D')
|
||||
{
|
||||
if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A')
|
||||
{
|
||||
dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int));
|
||||
#endif
|
||||
if(header.version != 3 && header.version != 4)
|
||||
{
|
||||
dbg_msg("datafile", "wrong version. version=%x", header.version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read in the rest except the data */
|
||||
size = 0;
|
||||
size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE);
|
||||
size += (header.num_items+header.num_raw_data)*sizeof(int);
|
||||
if(header.version == 4)
|
||||
size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */
|
||||
size += header.item_size;
|
||||
|
||||
allocsize = size;
|
||||
allocsize += sizeof(DATAFILE); /* add space for info structure */
|
||||
allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */
|
||||
|
||||
df = (DATAFILE*)mem_alloc(allocsize, 1);
|
||||
df->header = header;
|
||||
df->data_start_offset = sizeof(DATAFILE_HEADER) + size;
|
||||
df->data_ptrs = (char**)(df+1);
|
||||
df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *);
|
||||
df->file = file;
|
||||
|
||||
/* clear the data pointers */
|
||||
mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*));
|
||||
|
||||
/* read types, offsets, sizes and item data */
|
||||
readsize = io_read(file, df->data, size);
|
||||
if(readsize != size)
|
||||
{
|
||||
dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
swap_endian(df->data, sizeof(int), header.swaplen / sizeof(int));
|
||||
#endif
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
dbg_msg("datafile", "allocsize=%d", allocsize);
|
||||
dbg_msg("datafile", "readsize=%d", readsize);
|
||||
dbg_msg("datafile", "swaplen=%d", header.swaplen);
|
||||
dbg_msg("datafile", "item_size=%d", df->header.item_size);
|
||||
}
|
||||
|
||||
df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data;
|
||||
df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types];
|
||||
df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items];
|
||||
df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data];
|
||||
|
||||
if(header.version == 4)
|
||||
df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data];
|
||||
else
|
||||
df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data];
|
||||
df->info.data_start = df->info.item_start + df->header.item_size;
|
||||
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "datafile loading done. datafile='%s'", filename);
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
/*
|
||||
for(int i = 0; i < df->data.num_raw_data; i++)
|
||||
{
|
||||
void *p = datafile_get_data(df, i);
|
||||
dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size);
|
||||
}
|
||||
|
||||
for(int i = 0; i < datafile_num_items(df); i++)
|
||||
{
|
||||
int type, id;
|
||||
void *data = datafile_get_item(df, i, &type, &id);
|
||||
dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]);
|
||||
int *idata = (int*)data;
|
||||
for(int k = 0; k < 3; k++)
|
||||
dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]);
|
||||
}
|
||||
|
||||
for(int i = 0; i < df->data.num_item_types; i++)
|
||||
{
|
||||
dbg_msg("map", "\t%d: type=%x start=%d num=%d", i,
|
||||
df->info.item_types[i].type,
|
||||
df->info.item_types[i].start,
|
||||
df->info.item_types[i].num);
|
||||
for(int k = 0; k < df->info.item_types[i].num; k++)
|
||||
{
|
||||
int type, id;
|
||||
datafile_get_item(df, df->info.item_types[i].start+k, &type, &id);
|
||||
if(type != df->info.item_types[i].type)
|
||||
dbg_msg("map", "\tERROR");
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
int datafile_num_data(DATAFILE *df)
|
||||
{
|
||||
return df->header.num_raw_data;
|
||||
}
|
||||
|
||||
/* always returns the size in the file */
|
||||
int datafile_get_datasize(DATAFILE *df, int index)
|
||||
{
|
||||
if(index == df->header.num_raw_data-1)
|
||||
return df->header.data_size-df->info.data_offsets[index];
|
||||
return df->info.data_offsets[index+1]-df->info.data_offsets[index];
|
||||
}
|
||||
|
||||
static void *datafile_get_data_impl(DATAFILE *df, int index, int swap)
|
||||
{
|
||||
/* load it if needed */
|
||||
if(!df->data_ptrs[index])
|
||||
{
|
||||
/* fetch the data size */
|
||||
int datasize = datafile_get_datasize(df, index);
|
||||
int swapsize = datasize;
|
||||
|
||||
if(df->header.version == 4)
|
||||
{
|
||||
/* v4 has compressed data */
|
||||
void *temp = (char *)mem_alloc(datasize, 1);
|
||||
unsigned long uncompressed_size = df->info.data_sizes[index];
|
||||
unsigned long s;
|
||||
|
||||
dbg_msg("datafile", "loading data index=%d size=%d uncompressed=%d", index, datasize, uncompressed_size);
|
||||
df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1);
|
||||
|
||||
/* read the compressed data */
|
||||
io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START);
|
||||
io_read(df->file, temp, datasize);
|
||||
|
||||
/* decompress the data, TODO: check for errors */
|
||||
s = uncompressed_size;
|
||||
uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize);
|
||||
swapsize = s;
|
||||
|
||||
/* clean up the temporary buffers */
|
||||
mem_free(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* load the data */
|
||||
dbg_msg("datafile", "loading data index=%d size=%d", index, datasize);
|
||||
df->data_ptrs[index] = (char *)mem_alloc(datasize, 1);
|
||||
io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START);
|
||||
io_read(df->file, df->data_ptrs[index], datasize);
|
||||
}
|
||||
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
if(swap && swapsize)
|
||||
swap_endian(df->data_ptrs[index], sizeof(int), swapsize/sizeof(int));
|
||||
#endif
|
||||
}
|
||||
|
||||
return df->data_ptrs[index];
|
||||
}
|
||||
|
||||
void *datafile_get_data(DATAFILE *df, int index)
|
||||
{
|
||||
return datafile_get_data_impl(df, index, 0);
|
||||
}
|
||||
|
||||
void *datafile_get_data_swapped(DATAFILE *df, int index)
|
||||
{
|
||||
return datafile_get_data_impl(df, index, 1);
|
||||
}
|
||||
|
||||
void datafile_unload_data(DATAFILE *df, int index)
|
||||
{
|
||||
if(index < 0)
|
||||
return;
|
||||
|
||||
/* */
|
||||
mem_free(df->data_ptrs[index]);
|
||||
df->data_ptrs[index] = 0x0;
|
||||
}
|
||||
|
||||
int datafile_get_itemsize(DATAFILE *df, int index)
|
||||
{
|
||||
if(index == df->header.num_items-1)
|
||||
return df->header.item_size-df->info.item_offsets[index];
|
||||
return df->info.item_offsets[index+1]-df->info.item_offsets[index];
|
||||
}
|
||||
|
||||
void *datafile_get_item(DATAFILE *df, int index, int *type, int *id)
|
||||
{
|
||||
DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]);
|
||||
if(type)
|
||||
*type = (i->type_and_id>>16)&0xffff; /* remove sign extention */
|
||||
if(id)
|
||||
*id = i->type_and_id&0xffff;
|
||||
return (void *)(i+1);
|
||||
}
|
||||
|
||||
void datafile_get_type(DATAFILE *df, int type, int *start, int *num)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < df->header.num_item_types; i++)
|
||||
{
|
||||
if(df->info.item_types[i].type == type)
|
||||
{
|
||||
*start = df->info.item_types[i].start;
|
||||
*num = df->info.item_types[i].num;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*start = 0;
|
||||
*num = 0;
|
||||
}
|
||||
|
||||
void *datafile_find_item(DATAFILE *df, int type, int id)
|
||||
{
|
||||
int start, num,i ;
|
||||
int item_id;
|
||||
void *item;
|
||||
|
||||
datafile_get_type(df, type, &start, &num);
|
||||
for(i = 0; i < num; i++)
|
||||
{
|
||||
item = datafile_get_item(df, start+i,0, &item_id);
|
||||
if(id == item_id)
|
||||
return item;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int datafile_num_items(DATAFILE *df)
|
||||
{
|
||||
return df->header.num_items;
|
||||
}
|
||||
|
||||
void datafile_unload(DATAFILE *df)
|
||||
{
|
||||
if(df)
|
||||
{
|
||||
/* free the data that is loaded */
|
||||
int i;
|
||||
for(i = 0; i < df->header.num_raw_data; i++)
|
||||
mem_free(df->data_ptrs[i]);
|
||||
|
||||
io_close(df->file);
|
||||
mem_free(df);
|
||||
}
|
||||
}
|
||||
|
||||
/* DATAFILE output */
|
||||
typedef struct
|
||||
{
|
||||
int uncompressed_size;
|
||||
int compressed_size;
|
||||
void *compressed_data;
|
||||
} DATA_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
int id;
|
||||
int size;
|
||||
int next;
|
||||
int prev;
|
||||
void *data;
|
||||
} ITEM_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int num;
|
||||
int first;
|
||||
int last;
|
||||
} ITEMTYPE_INFO;
|
||||
|
||||
/* */
|
||||
struct DATAFILE_OUT_t
|
||||
{
|
||||
IOHANDLE file;
|
||||
int num_items;
|
||||
int num_datas;
|
||||
int num_item_types;
|
||||
ITEMTYPE_INFO item_types[0xffff];
|
||||
ITEM_INFO items[1024];
|
||||
DATA_INFO datas[1024];
|
||||
};
|
||||
|
||||
DATAFILE_OUT *datafile_create(const char *filename)
|
||||
{
|
||||
int i;
|
||||
DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1);
|
||||
df->file = engine_openfile(filename, IOFLAG_WRITE);
|
||||
if(!df->file)
|
||||
{
|
||||
mem_free(df);
|
||||
return 0;
|
||||
}
|
||||
|
||||
df->num_items = 0;
|
||||
df->num_datas = 0;
|
||||
df->num_item_types = 0;
|
||||
mem_zero(&df->item_types, sizeof(df->item_types));
|
||||
|
||||
for(i = 0; i < 0xffff; i++)
|
||||
{
|
||||
df->item_types[i].first = -1;
|
||||
df->item_types[i].last = -1;
|
||||
}
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data)
|
||||
{
|
||||
df->items[df->num_items].type = type;
|
||||
df->items[df->num_items].id = id;
|
||||
df->items[df->num_items].size = size;
|
||||
|
||||
/*
|
||||
dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size);
|
||||
int i;
|
||||
for(i = 0; i < size/4; i++)
|
||||
dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]);
|
||||
*/
|
||||
|
||||
/* copy data */
|
||||
df->items[df->num_items].data = mem_alloc(size, 1);
|
||||
mem_copy(df->items[df->num_items].data, data, size);
|
||||
|
||||
if(!df->item_types[type].num) /* count item types */
|
||||
df->num_item_types++;
|
||||
|
||||
/* link */
|
||||
df->items[df->num_items].prev = df->item_types[type].last;
|
||||
df->items[df->num_items].next = -1;
|
||||
|
||||
if(df->item_types[type].last != -1)
|
||||
df->items[df->item_types[type].last].next = df->num_items;
|
||||
df->item_types[type].last = df->num_items;
|
||||
|
||||
if(df->item_types[type].first == -1)
|
||||
df->item_types[type].first = df->num_items;
|
||||
|
||||
df->item_types[type].num++;
|
||||
|
||||
df->num_items++;
|
||||
return df->num_items-1;
|
||||
}
|
||||
|
||||
int datafile_add_data(DATAFILE_OUT *df, int size, void *data)
|
||||
{
|
||||
DATA_INFO *info = &df->datas[df->num_datas];
|
||||
unsigned long s = compressBound(size);
|
||||
void *compdata = mem_alloc(s, 1); /* temporary buffer that we use duing compression */
|
||||
|
||||
int result = compress((Bytef*)compdata, &s, (Bytef*)data, size);
|
||||
if(result != Z_OK)
|
||||
{
|
||||
dbg_msg("datafile", "compression error %d", result);
|
||||
dbg_assert(0, "zlib error");
|
||||
}
|
||||
|
||||
info->uncompressed_size = size;
|
||||
info->compressed_size = (int)s;
|
||||
info->compressed_data = mem_alloc(info->compressed_size, 1);
|
||||
mem_copy(info->compressed_data, compdata, info->compressed_size);
|
||||
mem_free(compdata);
|
||||
|
||||
df->num_datas++;
|
||||
return df->num_datas-1;
|
||||
}
|
||||
|
||||
int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data)
|
||||
{
|
||||
#if defined(CONF_ARCH_ENDIAN_BIG)
|
||||
void *swapped = mem_alloc(size, 1); /* temporary buffer that we use duing compression */
|
||||
int index;
|
||||
mem_copy(swapped, data, size);
|
||||
swap_endian(&swapped, sizeof(int), size/sizeof(int));
|
||||
index = datafile_add_data(df, size, swapped);
|
||||
mem_free(swapped);
|
||||
return index;
|
||||
#else
|
||||
return datafile_add_data(df, size, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int datafile_finish(DATAFILE_OUT *df)
|
||||
{
|
||||
int itemsize = 0;
|
||||
int i, count, offset;
|
||||
int typessize, headersize, offsetsize, filesize, swapsize;
|
||||
int datasize = 0;
|
||||
DATAFILE_ITEM_TYPE info;
|
||||
DATAFILE_ITEM itm;
|
||||
DATAFILE_HEADER header;
|
||||
|
||||
/* we should now write this file! */
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing");
|
||||
|
||||
/* calculate sizes */
|
||||
for(i = 0; i < df->num_items; i++)
|
||||
{
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM));
|
||||
itemsize += df->items[i].size + sizeof(DATAFILE_ITEM);
|
||||
}
|
||||
|
||||
|
||||
for(i = 0; i < df->num_datas; i++)
|
||||
datasize += df->datas[i].compressed_size;
|
||||
|
||||
/* calculate the complete size */
|
||||
typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE);
|
||||
headersize = sizeof(DATAFILE_HEADER);
|
||||
offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int);
|
||||
filesize = headersize + typessize + offsetsize + itemsize + datasize;
|
||||
swapsize = filesize - datasize;
|
||||
|
||||
(void)swapsize;
|
||||
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize);
|
||||
|
||||
/* construct header */
|
||||
{
|
||||
header.id[0] = 'D';
|
||||
header.id[1] = 'A';
|
||||
header.id[2] = 'T';
|
||||
header.id[3] = 'A';
|
||||
header.version = 4;
|
||||
header.size = filesize - 16;
|
||||
header.swaplen = swapsize - 16;
|
||||
header.num_item_types = df->num_item_types;
|
||||
header.num_items = df->num_items;
|
||||
header.num_raw_data = df->num_datas;
|
||||
header.item_size = itemsize;
|
||||
header.data_size = datasize;
|
||||
|
||||
/* TODO: apply swapping */
|
||||
/* write header */
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "headersize=%d", sizeof(header));
|
||||
io_write(df->file, &header, sizeof(header));
|
||||
}
|
||||
|
||||
/* write types */
|
||||
for(i = 0, count = 0; i < 0xffff; i++)
|
||||
{
|
||||
if(df->item_types[i].num)
|
||||
{
|
||||
/* write info */
|
||||
info.type = i;
|
||||
info.start = count;
|
||||
info.num = df->item_types[i].num;
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num);
|
||||
io_write(df->file, &info, sizeof(info));
|
||||
count += df->item_types[i].num;
|
||||
}
|
||||
}
|
||||
|
||||
/* write item offsets */
|
||||
for(i = 0, offset = 0; i < 0xffff; i++)
|
||||
{
|
||||
if(df->item_types[i].num)
|
||||
{
|
||||
/* write all items in of this type */
|
||||
int k = df->item_types[i].first;
|
||||
while(k != -1)
|
||||
{
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset);
|
||||
io_write(df->file, &offset, sizeof(offset));
|
||||
offset += df->items[k].size + sizeof(DATAFILE_ITEM);
|
||||
|
||||
/* next */
|
||||
k = df->items[k].next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write data offsets */
|
||||
for(i = 0, offset = 0; i < df->num_datas; i++)
|
||||
{
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset);
|
||||
io_write(df->file, &offset, sizeof(offset));
|
||||
offset += df->datas[i].compressed_size;
|
||||
}
|
||||
|
||||
/* write data uncompressed sizes */
|
||||
for(i = 0, offset = 0; i < df->num_datas; i++)
|
||||
{
|
||||
/*
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset);
|
||||
*/
|
||||
io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int));
|
||||
}
|
||||
|
||||
/* write items */
|
||||
for(i = 0; i < 0xffff; i++)
|
||||
{
|
||||
if(df->item_types[i].num)
|
||||
{
|
||||
/* write all items in of this type */
|
||||
int k = df->item_types[i].first;
|
||||
while(k != -1)
|
||||
{
|
||||
itm.type_and_id = (i<<16)|df->items[k].id;
|
||||
itm.size = df->items[k].size;
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size);
|
||||
|
||||
io_write(df->file, &itm, sizeof(itm));
|
||||
io_write(df->file, df->items[k].data, df->items[k].size);
|
||||
|
||||
/* next */
|
||||
k = df->items[k].next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write data */
|
||||
for(i = 0; i < df->num_datas; i++)
|
||||
{
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size);
|
||||
io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size);
|
||||
}
|
||||
|
||||
/* free data */
|
||||
for(i = 0; i < df->num_items; i++)
|
||||
mem_free(df->items[i].data);
|
||||
|
||||
|
||||
io_close(df->file);
|
||||
mem_free(df);
|
||||
|
||||
if(DEBUG)
|
||||
dbg_msg("datafile", "done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE 64*1024
|
||||
|
||||
int datafile_crc(const char *filename)
|
||||
{
|
||||
unsigned char buffer[BUFFER_SIZE];
|
||||
IOHANDLE file;
|
||||
int crc = 0;
|
||||
unsigned bytes = 0;
|
||||
|
||||
file = engine_openfile(filename, IOFLAG_READ);
|
||||
if(!file)
|
||||
return 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
bytes = io_read(file, buffer, BUFFER_SIZE);
|
||||
if(bytes <= 0)
|
||||
break;
|
||||
crc = crc32(crc, buffer, bytes);
|
||||
}
|
||||
|
||||
io_close(file);
|
||||
|
||||
return crc;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifndef __DATAFILE__H__
|
||||
#define __DATAFILE__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* raw datafile access */
|
||||
typedef struct DATAFILE_t DATAFILE;
|
||||
|
||||
/* read access */
|
||||
DATAFILE *datafile_load(const char *filename);
|
||||
DATAFILE *datafile_load_old(const char *filename);
|
||||
void *datafile_get_data(DATAFILE *df, int index);
|
||||
void *datafile_get_data_swapped(DATAFILE *df, int index); /* makes sure that the data is 32bit LE ints when saved */
|
||||
int datafile_get_datasize(DATAFILE *df, int index);
|
||||
void datafile_unload_data(DATAFILE *df, int index);
|
||||
void *datafile_get_item(DATAFILE *df, int index, int *type, int *id);
|
||||
int datafile_get_itemsize(DATAFILE *df, int index);
|
||||
void datafile_get_type(DATAFILE *df, int type, int *start, int *num);
|
||||
void *datafile_find_item(DATAFILE *df, int type, int id);
|
||||
int datafile_num_items(DATAFILE *df);
|
||||
int datafile_num_data(DATAFILE *df);
|
||||
void datafile_unload(DATAFILE *df);
|
||||
|
||||
int datafile_crc(const char *filename);
|
||||
|
||||
/* write access */
|
||||
typedef struct DATAFILE_OUT_t DATAFILE_OUT;
|
||||
DATAFILE_OUT *datafile_create(const char *filename);
|
||||
int datafile_add_data(DATAFILE_OUT *df, int size, void *data);
|
||||
int datafile_add_data_swapped(DATAFILE_OUT *df, int size, void *data);
|
||||
int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data);
|
||||
int datafile_finish(DATAFILE_OUT *df);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,640 +0,0 @@
|
||||
|
||||
#include <base/system.h>
|
||||
#include "e_demorec.h"
|
||||
#include "e_memheap.h"
|
||||
#include "e_snapshot.h"
|
||||
#include "e_compression.h"
|
||||
#include "e_network.h"
|
||||
#include "e_engine.h"
|
||||
#include "e_if_other.h"
|
||||
#include "e_demorec.h"
|
||||
|
||||
static IOHANDLE record_file = 0;
|
||||
static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, 1};
|
||||
|
||||
/* Record */
|
||||
static int record_lasttickmarker = -1;
|
||||
static int record_lastkeyframe;
|
||||
static unsigned char record_lastsnapshotdata[MAX_SNAPSHOT_SIZE];
|
||||
|
||||
int demorec_isrecording() { return record_file != 0; }
|
||||
|
||||
int demorec_record_start(const char *filename, const char *netversion, const char *map, int crc, const char *type)
|
||||
{
|
||||
DEMOREC_HEADER header;
|
||||
if(record_file)
|
||||
return -1;
|
||||
|
||||
record_file = engine_openfile(filename, IOFLAG_WRITE);
|
||||
|
||||
if(!record_file)
|
||||
{
|
||||
dbg_msg("demorec/record", "Unable to open '%s' for recording", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* write header */
|
||||
mem_zero(&header, sizeof(header));
|
||||
mem_copy(header.marker, header_marker, sizeof(header.marker));
|
||||
str_copy(header.netversion, netversion, sizeof(header.netversion));
|
||||
str_copy(header.map, map, sizeof(header.map));
|
||||
str_copy(header.type, type, sizeof(header.type));
|
||||
header.crc[0] = (crc>>24)&0xff;
|
||||
header.crc[1] = (crc>>16)&0xff;
|
||||
header.crc[2] = (crc>>8)&0xff;
|
||||
header.crc[3] = (crc)&0xff;
|
||||
io_write(record_file, &header, sizeof(header));
|
||||
|
||||
record_lastkeyframe = -1;
|
||||
record_lasttickmarker = -1;
|
||||
|
||||
dbg_msg("demorec/record", "Recording to '%s'", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Tickmarker
|
||||
7 = Always set
|
||||
6 = Keyframe flag
|
||||
0-5 = Delta tick
|
||||
|
||||
Normal
|
||||
7 = Not set
|
||||
5-6 = Type
|
||||
0-4 = Size
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
CHUNKTYPEFLAG_TICKMARKER = 0x80,
|
||||
CHUNKTICKFLAG_KEYFRAME = 0x40, /* only when tickmarker is set*/
|
||||
|
||||
CHUNKMASK_TICK = 0x3f,
|
||||
CHUNKMASK_TYPE = 0x60,
|
||||
CHUNKMASK_SIZE = 0x1f,
|
||||
|
||||
CHUNKTYPE_SNAPSHOT = 1,
|
||||
CHUNKTYPE_MESSAGE = 2,
|
||||
CHUNKTYPE_DELTA = 3,
|
||||
|
||||
CHUNKFLAG_BIGSIZE = 0x10
|
||||
};
|
||||
|
||||
static void demorec_record_write_tickmarker(int tick, int keyframe)
|
||||
{
|
||||
if(record_lasttickmarker == -1 || tick-record_lasttickmarker > 63 || keyframe)
|
||||
{
|
||||
unsigned char chunk[5];
|
||||
chunk[0] = CHUNKTYPEFLAG_TICKMARKER;
|
||||
chunk[1] = (tick>>24)&0xff;
|
||||
chunk[2] = (tick>>16)&0xff;
|
||||
chunk[3] = (tick>>8)&0xff;
|
||||
chunk[4] = (tick)&0xff;
|
||||
|
||||
if(keyframe)
|
||||
chunk[0] |= CHUNKTICKFLAG_KEYFRAME;
|
||||
|
||||
io_write(record_file, chunk, sizeof(chunk));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char chunk[1];
|
||||
chunk[0] = CHUNKTYPEFLAG_TICKMARKER | (tick-record_lasttickmarker);
|
||||
io_write(record_file, chunk, sizeof(chunk));
|
||||
}
|
||||
|
||||
record_lasttickmarker = tick;
|
||||
}
|
||||
|
||||
static void demorec_record_write(int type, const void *data, int size)
|
||||
{
|
||||
char buffer[64*1024];
|
||||
char buffer2[64*1024];
|
||||
unsigned char chunk[3];
|
||||
|
||||
if(!record_file)
|
||||
return;
|
||||
|
||||
|
||||
/* pad the data with 0 so we get an alignment of 4,
|
||||
else the compression won't work and miss some bytes */
|
||||
mem_copy(buffer2, data, size);
|
||||
while(size&3)
|
||||
buffer2[size++] = 0;
|
||||
size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */
|
||||
size = netcommon_compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */
|
||||
|
||||
|
||||
chunk[0] = ((type&0x3)<<5);
|
||||
if(size < 30)
|
||||
{
|
||||
chunk[0] |= size;
|
||||
io_write(record_file, chunk, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(size < 256)
|
||||
{
|
||||
chunk[0] |= 30;
|
||||
chunk[1] = size&0xff;
|
||||
io_write(record_file, chunk, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk[0] |= 31;
|
||||
chunk[1] = size&0xff;
|
||||
chunk[2] = size>>8;
|
||||
io_write(record_file, chunk, 3);
|
||||
}
|
||||
}
|
||||
|
||||
io_write(record_file, buffer2, size);
|
||||
}
|
||||
|
||||
void demorec_record_snapshot(int tick, const void *data, int size)
|
||||
{
|
||||
if(record_lastkeyframe == -1 || (tick-record_lastkeyframe) > SERVER_TICK_SPEED*5)
|
||||
{
|
||||
/* write full tickmarker */
|
||||
demorec_record_write_tickmarker(tick, 1);
|
||||
|
||||
/* write snapshot */
|
||||
demorec_record_write(CHUNKTYPE_SNAPSHOT, data, size);
|
||||
|
||||
record_lastkeyframe = tick;
|
||||
mem_copy(record_lastsnapshotdata, data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* create delta, prepend tick */
|
||||
char delta_data[MAX_SNAPSHOT_SIZE+sizeof(int)];
|
||||
int delta_size;
|
||||
|
||||
/* write tickmarker */
|
||||
demorec_record_write_tickmarker(tick, 0);
|
||||
|
||||
delta_size = snapshot_create_delta((SNAPSHOT*)record_lastsnapshotdata, (SNAPSHOT*)data, &delta_data);
|
||||
if(delta_size)
|
||||
{
|
||||
/* record delta */
|
||||
demorec_record_write(CHUNKTYPE_DELTA, delta_data, delta_size);
|
||||
mem_copy(record_lastsnapshotdata, data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void demorec_record_message(const void *data, int size)
|
||||
{
|
||||
demorec_record_write(CHUNKTYPE_MESSAGE, data, size);
|
||||
}
|
||||
|
||||
int demorec_record_stop()
|
||||
{
|
||||
if(!record_file)
|
||||
return -1;
|
||||
|
||||
dbg_msg("demorec/record", "Stopped recording");
|
||||
io_close(record_file);
|
||||
record_file = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Playback */
|
||||
typedef struct KEYFRAME
|
||||
{
|
||||
long filepos;
|
||||
int tick;
|
||||
} KEYFRAME;
|
||||
|
||||
typedef struct KEYFRAME_SEARCH
|
||||
{
|
||||
KEYFRAME frame;
|
||||
struct KEYFRAME_SEARCH *next;
|
||||
} KEYFRAME_SEARCH;
|
||||
|
||||
static IOHANDLE play_file = 0;
|
||||
static DEMOREC_PLAYCALLBACK play_callback_snapshot = 0;
|
||||
static DEMOREC_PLAYCALLBACK play_callback_message = 0;
|
||||
static KEYFRAME *keyframes = 0;
|
||||
|
||||
static DEMOREC_PLAYBACKINFO playbackinfo;
|
||||
static unsigned char playback_lastsnapshotdata[MAX_SNAPSHOT_SIZE];
|
||||
static int playback_lastsnapshotdata_size = -1;
|
||||
|
||||
|
||||
const DEMOREC_PLAYBACKINFO *demorec_playback_info() { return &playbackinfo; }
|
||||
int demorec_isplaying() { return play_file != 0; }
|
||||
|
||||
int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb)
|
||||
{
|
||||
play_callback_snapshot = snapshot_cb;
|
||||
play_callback_message = message_cb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_chunk_header(int *type, int *size, int *tick)
|
||||
{
|
||||
unsigned char chunk = 0;
|
||||
|
||||
*size = 0;
|
||||
*type = 0;
|
||||
|
||||
if(io_read(play_file, &chunk, sizeof(chunk)) != sizeof(chunk))
|
||||
return -1;
|
||||
|
||||
if(chunk&CHUNKTYPEFLAG_TICKMARKER)
|
||||
{
|
||||
/* decode tick marker */
|
||||
int tickdelta = chunk&(CHUNKMASK_TICK);
|
||||
*type = chunk&(CHUNKTYPEFLAG_TICKMARKER|CHUNKTICKFLAG_KEYFRAME);
|
||||
|
||||
if(tickdelta == 0)
|
||||
{
|
||||
unsigned char tickdata[4];
|
||||
if(io_read(play_file, tickdata, sizeof(tickdata)) != sizeof(tickdata))
|
||||
return -1;
|
||||
*tick = (tickdata[0]<<24) | (tickdata[1]<<16) | (tickdata[2]<<8) | tickdata[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
*tick += tickdelta;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* decode normal chunk */
|
||||
*type = (chunk&CHUNKMASK_TYPE)>>5;
|
||||
*size = chunk&CHUNKMASK_SIZE;
|
||||
|
||||
if(*size == 30)
|
||||
{
|
||||
unsigned char sizedata[1];
|
||||
if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata))
|
||||
return -1;
|
||||
*size = sizedata[0];
|
||||
|
||||
}
|
||||
else if(*size == 31)
|
||||
{
|
||||
unsigned char sizedata[2];
|
||||
if(io_read(play_file, sizedata, sizeof(sizedata)) != sizeof(sizedata))
|
||||
return -1;
|
||||
*size = (sizedata[1]<<8) | sizedata[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scan_file()
|
||||
{
|
||||
long start_pos;
|
||||
HEAP *heap = 0;
|
||||
KEYFRAME_SEARCH *first_key = 0;
|
||||
KEYFRAME_SEARCH *current_key = 0;
|
||||
/*DEMOREC_CHUNK chunk;*/
|
||||
int chunk_size, chunk_type, chunk_tick = 0;
|
||||
int i;
|
||||
|
||||
heap = memheap_create();
|
||||
|
||||
start_pos = io_tell(play_file);
|
||||
playbackinfo.seekable_points = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
long current_pos = io_tell(play_file);
|
||||
|
||||
if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick))
|
||||
break;
|
||||
|
||||
/* read the chunk */
|
||||
if(chunk_type&CHUNKTYPEFLAG_TICKMARKER)
|
||||
{
|
||||
if(chunk_type&CHUNKTICKFLAG_KEYFRAME)
|
||||
{
|
||||
KEYFRAME_SEARCH *key;
|
||||
|
||||
/* save the position */
|
||||
key = (KEYFRAME_SEARCH*)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH));
|
||||
key->frame.filepos = current_pos;
|
||||
key->frame.tick = chunk_tick;
|
||||
key->next = 0;
|
||||
if(current_key)
|
||||
current_key->next = key;
|
||||
if(!first_key)
|
||||
first_key = key;
|
||||
current_key = key;
|
||||
playbackinfo.seekable_points++;
|
||||
}
|
||||
|
||||
if(playbackinfo.first_tick == -1)
|
||||
playbackinfo.first_tick = chunk_tick;
|
||||
playbackinfo.last_tick = chunk_tick;
|
||||
}
|
||||
else if(chunk_size)
|
||||
io_skip(play_file, chunk_size);
|
||||
|
||||
}
|
||||
|
||||
/* copy all the frames to an array instead for fast access */
|
||||
keyframes = (KEYFRAME*)mem_alloc(playbackinfo.seekable_points*sizeof(KEYFRAME), 1);
|
||||
for(current_key = first_key, i = 0; current_key; current_key = current_key->next, i++)
|
||||
keyframes[i] = current_key->frame;
|
||||
|
||||
/* destroy the temporary heap and seek back to the start */
|
||||
memheap_destroy(heap);
|
||||
io_seek(play_file, start_pos, IOSEEK_START);
|
||||
}
|
||||
|
||||
static void do_tick()
|
||||
{
|
||||
static char compresseddata[MAX_SNAPSHOT_SIZE];
|
||||
static char decompressed[MAX_SNAPSHOT_SIZE];
|
||||
static char data[MAX_SNAPSHOT_SIZE];
|
||||
int chunk_size, chunk_type, chunk_tick;
|
||||
int data_size;
|
||||
int got_snapshot = 0;
|
||||
|
||||
/* update ticks */
|
||||
playbackinfo.previous_tick = playbackinfo.current_tick;
|
||||
playbackinfo.current_tick = playbackinfo.next_tick;
|
||||
chunk_tick = playbackinfo.current_tick;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if(read_chunk_header(&chunk_type, &chunk_size, &chunk_tick))
|
||||
{
|
||||
/* stop on error or eof */
|
||||
dbg_msg("demorec", "end of file");
|
||||
demorec_playback_pause();
|
||||
break;
|
||||
}
|
||||
|
||||
/* read the chunk */
|
||||
if(chunk_size)
|
||||
{
|
||||
if(io_read(play_file, compresseddata, chunk_size) != chunk_size)
|
||||
{
|
||||
/* stop on error or eof */
|
||||
dbg_msg("demorec", "error reading chunk");
|
||||
demorec_playback_stop();
|
||||
break;
|
||||
}
|
||||
|
||||
data_size = netcommon_decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed));
|
||||
if(data_size < 0)
|
||||
{
|
||||
/* stop on error or eof */
|
||||
dbg_msg("demorec", "error during network decompression");
|
||||
demorec_playback_stop();
|
||||
break;
|
||||
}
|
||||
|
||||
data_size = intpack_decompress(decompressed, data_size, data);
|
||||
|
||||
if(data_size < 0)
|
||||
{
|
||||
dbg_msg("demorec", "error during intpack decompression");
|
||||
demorec_playback_stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(chunk_type == CHUNKTYPE_DELTA)
|
||||
{
|
||||
/* process delta snapshot */
|
||||
static char newsnap[MAX_SNAPSHOT_SIZE];
|
||||
|
||||
got_snapshot = 1;
|
||||
|
||||
data_size = snapshot_unpack_delta((SNAPSHOT*)playback_lastsnapshotdata, (SNAPSHOT*)newsnap, data, data_size);
|
||||
|
||||
if(data_size >= 0)
|
||||
{
|
||||
if(play_callback_snapshot)
|
||||
play_callback_snapshot(newsnap, data_size);
|
||||
|
||||
playback_lastsnapshotdata_size = data_size;
|
||||
mem_copy(playback_lastsnapshotdata, newsnap, data_size);
|
||||
}
|
||||
else
|
||||
dbg_msg("demorec", "error duing unpacking of delta, err=%d", data_size);
|
||||
}
|
||||
else if(chunk_type == CHUNKTYPE_SNAPSHOT)
|
||||
{
|
||||
/* process full snapshot */
|
||||
got_snapshot = 1;
|
||||
|
||||
playback_lastsnapshotdata_size = data_size;
|
||||
mem_copy(playback_lastsnapshotdata, data, data_size);
|
||||
if(play_callback_snapshot)
|
||||
play_callback_snapshot(data, data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if there were no snapshots in this tick, replay the last one */
|
||||
if(!got_snapshot && play_callback_snapshot && playback_lastsnapshotdata_size != -1)
|
||||
{
|
||||
got_snapshot = 1;
|
||||
play_callback_snapshot(playback_lastsnapshotdata, playback_lastsnapshotdata_size);
|
||||
}
|
||||
|
||||
/* check the remaining types */
|
||||
if(chunk_type&CHUNKTYPEFLAG_TICKMARKER)
|
||||
{
|
||||
playbackinfo.next_tick = chunk_tick;
|
||||
break;
|
||||
}
|
||||
else if(chunk_type == CHUNKTYPE_MESSAGE)
|
||||
{
|
||||
if(play_callback_message)
|
||||
play_callback_message(data, data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void demorec_playback_pause()
|
||||
{
|
||||
playbackinfo.paused = 1;
|
||||
}
|
||||
|
||||
void demorec_playback_unpause()
|
||||
{
|
||||
if(playbackinfo.paused)
|
||||
{
|
||||
/*playbackinfo.start_tick = playbackinfo.current_tick;
|
||||
playbackinfo.start_time = time_get();*/
|
||||
playbackinfo.paused = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int demorec_playback_load(const char *filename)
|
||||
{
|
||||
play_file = engine_openfile(filename, IOFLAG_READ);
|
||||
if(!play_file)
|
||||
{
|
||||
dbg_msg("demorec/playback", "could not open '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* clear the playback info */
|
||||
mem_zero(&playbackinfo, sizeof(playbackinfo));
|
||||
playbackinfo.first_tick = -1;
|
||||
playbackinfo.last_tick = -1;
|
||||
/*playbackinfo.start_tick = -1;*/
|
||||
playbackinfo.next_tick = -1;
|
||||
playbackinfo.current_tick = -1;
|
||||
playbackinfo.previous_tick = -1;
|
||||
playbackinfo.speed = 1;
|
||||
|
||||
playback_lastsnapshotdata_size = -1;
|
||||
|
||||
/* read the header */
|
||||
io_read(play_file, &playbackinfo.header, sizeof(playbackinfo.header));
|
||||
if(mem_comp(playbackinfo.header.marker, header_marker, sizeof(header_marker)) != 0)
|
||||
{
|
||||
dbg_msg("demorec/playback", "'%s' is not a demo file", filename);
|
||||
io_close(play_file);
|
||||
play_file = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* scan the file for interessting points */
|
||||
scan_file();
|
||||
|
||||
/* ready for playback */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int demorec_playback_nextframe()
|
||||
{
|
||||
do_tick();
|
||||
return demorec_isplaying();
|
||||
}
|
||||
|
||||
int demorec_playback_play()
|
||||
{
|
||||
/* fill in previous and next tick */
|
||||
while(playbackinfo.previous_tick == -1 && demorec_isplaying())
|
||||
do_tick();
|
||||
|
||||
/* set start info */
|
||||
/*playbackinfo.start_tick = playbackinfo.previous_tick;
|
||||
playbackinfo.start_time = time_get();*/
|
||||
playbackinfo.current_time = playbackinfo.previous_tick*time_freq()/SERVER_TICK_SPEED;
|
||||
playbackinfo.last_update = time_get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int demorec_playback_set(float percent)
|
||||
{
|
||||
int keyframe;
|
||||
int wanted_tick;
|
||||
if(!play_file)
|
||||
return -1;
|
||||
|
||||
/* -5 because we have to have a current tick and previous tick when we do the playback */
|
||||
wanted_tick = playbackinfo.first_tick + (int)((playbackinfo.last_tick-playbackinfo.first_tick)*percent) - 5;
|
||||
|
||||
keyframe = (int)(playbackinfo.seekable_points*percent);
|
||||
|
||||
if(keyframe < 0 || keyframe >= playbackinfo.seekable_points)
|
||||
return -1;
|
||||
|
||||
/* get correct key frame */
|
||||
if(keyframes[keyframe].tick < wanted_tick)
|
||||
while(keyframe < playbackinfo.seekable_points-1 && keyframes[keyframe].tick < wanted_tick)
|
||||
keyframe++;
|
||||
|
||||
while(keyframe && keyframes[keyframe].tick > wanted_tick)
|
||||
keyframe--;
|
||||
|
||||
/* seek to the correct keyframe */
|
||||
io_seek(play_file, keyframes[keyframe].filepos, IOSEEK_START);
|
||||
|
||||
/*playbackinfo.start_tick = -1;*/
|
||||
playbackinfo.next_tick = -1;
|
||||
playbackinfo.current_tick = -1;
|
||||
playbackinfo.previous_tick = -1;
|
||||
|
||||
/* playback everything until we hit our tick */
|
||||
while(playbackinfo.previous_tick < wanted_tick)
|
||||
do_tick();
|
||||
|
||||
demorec_playback_play();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void demorec_playback_setspeed(float speed)
|
||||
{
|
||||
playbackinfo.speed = speed;
|
||||
}
|
||||
|
||||
int demorec_playback_update()
|
||||
{
|
||||
int64 now = time_get();
|
||||
int64 deltatime = now-playbackinfo.last_update;
|
||||
playbackinfo.last_update = now;
|
||||
|
||||
if(!demorec_isplaying())
|
||||
return 0;
|
||||
|
||||
if(playbackinfo.paused)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
int64 freq = time_freq();
|
||||
playbackinfo.current_time += (int64)(deltatime*(double)playbackinfo.speed);
|
||||
|
||||
while(1)
|
||||
{
|
||||
int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED;
|
||||
|
||||
/* break if we are ready */
|
||||
if(curtick_start > playbackinfo.current_time)
|
||||
break;
|
||||
|
||||
/* do one more tick */
|
||||
do_tick();
|
||||
|
||||
if(playbackinfo.paused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* update intratick */
|
||||
{
|
||||
int64 curtick_start = (playbackinfo.current_tick)*freq/SERVER_TICK_SPEED;
|
||||
int64 prevtick_start = (playbackinfo.previous_tick)*freq/SERVER_TICK_SPEED;
|
||||
playbackinfo.intratick = (playbackinfo.current_time - prevtick_start) / (float)(curtick_start-prevtick_start);
|
||||
playbackinfo.ticktime = (playbackinfo.current_time - prevtick_start) / (float)freq;
|
||||
}
|
||||
|
||||
if(playbackinfo.current_tick == playbackinfo.previous_tick ||
|
||||
playbackinfo.current_tick == playbackinfo.next_tick)
|
||||
{
|
||||
dbg_msg("demorec/playback", "tick error prev=%d cur=%d next=%d",
|
||||
playbackinfo.previous_tick, playbackinfo.current_tick, playbackinfo.next_tick);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int demorec_playback_stop()
|
||||
{
|
||||
if(!play_file)
|
||||
return -1;
|
||||
|
||||
dbg_msg("demorec/playback", "Stopped playback");
|
||||
io_close(play_file);
|
||||
play_file = 0;
|
||||
mem_free(keyframes);
|
||||
keyframes = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef _DEMOREC_H
|
||||
#define _DEMOREC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
typedef struct DEMOREC_HEADER
|
||||
{
|
||||
char marker[8];
|
||||
char netversion[64];
|
||||
char map[64];
|
||||
unsigned char crc[4];
|
||||
char type[8];
|
||||
} DEMOREC_HEADER;
|
||||
|
||||
typedef struct DEMOREC_CHUNK
|
||||
{
|
||||
char type[2];
|
||||
unsigned short size;
|
||||
} DEMOREC_CHUNK;
|
||||
|
||||
typedef struct DEMOREC_TICKMARKER
|
||||
{
|
||||
int tick;
|
||||
} DEMOREC_TICKMARKER;
|
||||
|
||||
typedef struct DEMOREC_PLAYBACKINFO
|
||||
{
|
||||
DEMOREC_HEADER header;
|
||||
|
||||
int paused;
|
||||
float speed;
|
||||
|
||||
int64 last_update;
|
||||
int64 current_time;
|
||||
|
||||
int first_tick;
|
||||
int last_tick;
|
||||
|
||||
int seekable_points;
|
||||
|
||||
int next_tick;
|
||||
int current_tick;
|
||||
int previous_tick;
|
||||
|
||||
float intratick;
|
||||
float ticktime;
|
||||
} DEMOREC_PLAYBACKINFO;
|
||||
|
||||
int demorec_record_start(const char *filename, const char *netversion, const char *map, int map_crc, const char *type);
|
||||
int demorec_isrecording();
|
||||
void demorec_record_snapshot(int tick, const void *data, int size);
|
||||
void demorec_record_message(const void *data, int size);
|
||||
int demorec_record_stop();
|
||||
|
||||
typedef void (*DEMOREC_PLAYCALLBACK)(void *data, int size);
|
||||
|
||||
int demorec_playback_registercallbacks(DEMOREC_PLAYCALLBACK snapshot_cb, DEMOREC_PLAYCALLBACK message_cb);
|
||||
int demorec_playback_load(const char *filename);
|
||||
int demorec_playback_nextframe();
|
||||
int demorec_playback_play();
|
||||
void demorec_playback_pause();
|
||||
void demorec_playback_unpause();
|
||||
void demorec_playback_setspeed(float speed);
|
||||
int demorec_playback_set(float precent);
|
||||
int demorec_playback_update();
|
||||
const DEMOREC_PLAYBACKINFO *demorec_playback_info();
|
||||
int demorec_isplaying();
|
||||
int demorec_playback_stop();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,596 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/e_server_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_console.h>
|
||||
#include <engine/e_engine.h>
|
||||
#include <engine/e_network.h>
|
||||
#include "e_linereader.h"
|
||||
|
||||
/* compiled-in data-dir path */
|
||||
#define DATA_DIR "data"
|
||||
|
||||
static JOBPOOL hostlookuppool;
|
||||
static int engine_find_datadir(char *argv0);
|
||||
|
||||
static void con_dbg_dumpmem(void *result, void *user_data)
|
||||
{
|
||||
mem_debug_dump();
|
||||
}
|
||||
|
||||
static void con_dbg_lognetwork(void *result, void *user_data)
|
||||
{
|
||||
netcommon_openlog("network_sent.dat", "network_recv.dat");
|
||||
}
|
||||
|
||||
|
||||
static char application_save_path[512] = {0};
|
||||
static char datadir[512] = {0};
|
||||
|
||||
const char *engine_savepath(const char *filename, char *buffer, int max)
|
||||
{
|
||||
str_format(buffer, max, "%s/%s", application_save_path, filename);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void engine_init(const char *appname)
|
||||
{
|
||||
dbg_logger_stdout();
|
||||
dbg_logger_debugger();
|
||||
|
||||
/* */
|
||||
dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
|
||||
#ifdef CONF_ARCH_ENDIAN_LITTLE
|
||||
dbg_msg("engine", "arch is little endian");
|
||||
#elif defined(CONF_ARCH_ENDIAN_BIG)
|
||||
dbg_msg("engine", "arch is big endian");
|
||||
#else
|
||||
dbg_msg("engine", "unknown endian");
|
||||
#endif
|
||||
|
||||
/* init the network */
|
||||
net_init();
|
||||
netcommon_init();
|
||||
|
||||
/* create storage location */
|
||||
{
|
||||
char path[1024] = {0};
|
||||
fs_storage_path(appname, application_save_path, sizeof(application_save_path));
|
||||
if(fs_makedir(application_save_path) == 0)
|
||||
{
|
||||
str_format(path, sizeof(path), "%s/screenshots", application_save_path);
|
||||
fs_makedir(path);
|
||||
|
||||
str_format(path, sizeof(path), "%s/maps", application_save_path);
|
||||
fs_makedir(path);
|
||||
|
||||
str_format(path, sizeof(path), "%s/downloadedmaps", application_save_path);
|
||||
fs_makedir(path);
|
||||
|
||||
str_format(path, sizeof(path), "%s/demos", application_save_path);
|
||||
fs_makedir(path);
|
||||
}
|
||||
}
|
||||
|
||||
/* init console and add the console logger */
|
||||
console_init();
|
||||
dbg_logger(console_print);
|
||||
|
||||
jobs_initpool(&hostlookuppool, 1);
|
||||
|
||||
MACRO_REGISTER_COMMAND("dbg_dumpmem", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_dumpmem, 0x0, "Dump the memory");
|
||||
MACRO_REGISTER_COMMAND("dbg_lognetwork", "", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_dbg_lognetwork, 0x0, "Log the network");
|
||||
|
||||
/* reset the config */
|
||||
config_reset();
|
||||
}
|
||||
|
||||
|
||||
void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
/* list current directory */
|
||||
if(types&LISTDIRTYPE_CURRENT)
|
||||
{
|
||||
fs_listdir(path, cb, user);
|
||||
}
|
||||
|
||||
/* list users directory */
|
||||
if(types&LISTDIRTYPE_SAVE)
|
||||
{
|
||||
engine_savepath(path, buffer, sizeof(buffer));
|
||||
fs_listdir(buffer, cb, user);
|
||||
}
|
||||
|
||||
/* list datadir directory */
|
||||
if(types&LISTDIRTYPE_DATA)
|
||||
{
|
||||
str_format(buffer, sizeof(buffer), "%s/%s", datadir, path);
|
||||
fs_listdir(buffer, cb, user);
|
||||
}
|
||||
}
|
||||
|
||||
void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags)
|
||||
{
|
||||
if(flags&IOFLAG_WRITE)
|
||||
engine_savepath(filename, buffer, buffer_size);
|
||||
else
|
||||
{
|
||||
IOHANDLE handle = 0;
|
||||
|
||||
/* check current directory */
|
||||
handle = io_open(filename, flags);
|
||||
if(handle)
|
||||
{
|
||||
str_copy(buffer, filename, buffer_size);
|
||||
io_close(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check user directory */
|
||||
engine_savepath(filename, buffer, buffer_size);
|
||||
handle = io_open(buffer, flags);
|
||||
if(handle)
|
||||
{
|
||||
io_close(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check normal data directory */
|
||||
str_format(buffer, buffer_size, "%s/%s", datadir, filename);
|
||||
handle = io_open(buffer, flags);
|
||||
if(handle)
|
||||
{
|
||||
io_close(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[0] = 0;
|
||||
}
|
||||
|
||||
IOHANDLE engine_openfile(const char *filename, int flags)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
if(flags&IOFLAG_WRITE)
|
||||
{
|
||||
engine_savepath(filename, buffer, sizeof(buffer));
|
||||
return io_open(buffer, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
IOHANDLE handle = 0;
|
||||
|
||||
/* check current directory */
|
||||
handle = io_open(filename, flags);
|
||||
if(handle)
|
||||
return handle;
|
||||
|
||||
/* check user directory */
|
||||
engine_savepath(filename, buffer, sizeof(buffer));
|
||||
handle = io_open(buffer, flags);
|
||||
if(handle)
|
||||
return handle;
|
||||
|
||||
/* check normal data directory */
|
||||
str_format(buffer, sizeof(buffer), "%s/%s", datadir, filename);
|
||||
handle = io_open(buffer, flags);
|
||||
if(handle)
|
||||
return handle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void engine_parse_arguments(int argc, char **argv)
|
||||
{
|
||||
/* load the configuration */
|
||||
int i;
|
||||
|
||||
/* check for datadir override */
|
||||
for(i = 1; i < argc; i++)
|
||||
{
|
||||
if(argv[i][0] == '-' && argv[i][1] == 'd' && argv[i][2] == 0 && argc - i > 1)
|
||||
{
|
||||
str_copy(datadir, argv[i+1], sizeof(datadir));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* search for data directory */
|
||||
engine_find_datadir(argv[0]);
|
||||
|
||||
dbg_msg("engine/datadir", "paths used:");
|
||||
dbg_msg("engine/datadir", "\t.");
|
||||
dbg_msg("engine/datadir", "\t%s", application_save_path);
|
||||
dbg_msg("engine/datadir", "\t%s", datadir);
|
||||
dbg_msg("engine/datadir", "saving files to: %s", application_save_path);
|
||||
|
||||
|
||||
/* check for scripts to execute */
|
||||
for(i = 1; i < argc; i++)
|
||||
{
|
||||
if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
|
||||
{
|
||||
console_execute_file(argv[i+1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* search arguments for overrides */
|
||||
{
|
||||
int i;
|
||||
for(i = 1; i < argc; i++)
|
||||
console_execute_line(argv[i]);
|
||||
}
|
||||
|
||||
console_execute_file("autoexec.cfg");
|
||||
|
||||
/* open logfile if needed */
|
||||
if(config.logfile[0])
|
||||
dbg_logger_file(config.logfile);
|
||||
|
||||
/* set default servers and load from disk*/
|
||||
mastersrv_default();
|
||||
mastersrv_load();
|
||||
}
|
||||
|
||||
|
||||
static IOHANDLE config_file = 0;
|
||||
|
||||
int engine_config_write_start()
|
||||
{
|
||||
config_save();
|
||||
config_file = engine_openfile("settings.cfg", IOFLAG_WRITE);
|
||||
if(config_file == 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void engine_config_write_line(const char *line)
|
||||
{
|
||||
if(config_file)
|
||||
{
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
static const char newline[] = "\r\n";
|
||||
#else
|
||||
static const char newline[] = "\n";
|
||||
#endif
|
||||
io_write(config_file, line, strlen(line));
|
||||
io_write(config_file, newline, sizeof(newline)-1);
|
||||
}
|
||||
}
|
||||
|
||||
void engine_config_write_stop()
|
||||
{
|
||||
io_close(config_file);
|
||||
config_file = 0;
|
||||
}
|
||||
/*
|
||||
void engine_writeconfig()
|
||||
{
|
||||
}*/
|
||||
|
||||
static int perf_tick = 1;
|
||||
static PERFORMACE_INFO *current = 0;
|
||||
|
||||
void perf_init()
|
||||
{
|
||||
}
|
||||
|
||||
void perf_next()
|
||||
{
|
||||
perf_tick++;
|
||||
current = 0;
|
||||
}
|
||||
|
||||
void perf_start(PERFORMACE_INFO *info)
|
||||
{
|
||||
if(info->tick != perf_tick)
|
||||
{
|
||||
info->parent = current;
|
||||
info->first_child = 0;
|
||||
info->next_child = 0;
|
||||
|
||||
if(info->parent)
|
||||
{
|
||||
info->next_child = info->parent->first_child;
|
||||
info->parent->first_child = info;
|
||||
}
|
||||
|
||||
info->tick = perf_tick;
|
||||
info->biggest = 0;
|
||||
info->total = 0;
|
||||
}
|
||||
|
||||
current = info;
|
||||
current->start = time_get();
|
||||
}
|
||||
|
||||
void perf_end()
|
||||
{
|
||||
if(!current)
|
||||
return;
|
||||
|
||||
current->last_delta = time_get()-current->start;
|
||||
current->total += current->last_delta;
|
||||
|
||||
if(current->last_delta > current->biggest)
|
||||
current->biggest = current->last_delta;
|
||||
|
||||
current = current->parent;
|
||||
}
|
||||
|
||||
static void perf_dump_imp(PERFORMACE_INFO *info, int indent)
|
||||
{
|
||||
char buf[512] = {0};
|
||||
int64 freq = time_freq();
|
||||
int i;
|
||||
|
||||
for(i = 0; i < indent; i++)
|
||||
buf[i] = ' ';
|
||||
|
||||
str_format(&buf[indent], sizeof(buf)-indent, "%-20s %8.2f %8.2f", info->name, info->total*1000/(float)freq, info->biggest*1000/(float)freq);
|
||||
dbg_msg("perf", "%s", buf);
|
||||
|
||||
info = info->first_child;
|
||||
while(info)
|
||||
{
|
||||
perf_dump_imp(info, indent+2);
|
||||
info = info->next_child;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_dump(PERFORMACE_INFO *top)
|
||||
{
|
||||
perf_dump_imp(top, 0);
|
||||
}
|
||||
|
||||
/* master server functions */
|
||||
typedef struct
|
||||
{
|
||||
char hostname[128];
|
||||
NETADDR addr;
|
||||
|
||||
HOSTLOOKUP lookup;
|
||||
} MASTER_INFO;
|
||||
|
||||
static MASTER_INFO master_servers[MAX_MASTERSERVERS] = {{{0}}};
|
||||
static int needs_update = -1;
|
||||
|
||||
int mastersrv_refresh_addresses()
|
||||
{
|
||||
int i;
|
||||
|
||||
if(needs_update != -1)
|
||||
return 0;
|
||||
|
||||
dbg_msg("engine/mastersrv", "refreshing master server addresses");
|
||||
|
||||
/* add lookup jobs */
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
engine_hostlookup(&master_servers[i].lookup, master_servers[i].hostname);
|
||||
|
||||
needs_update = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mastersrv_update()
|
||||
{
|
||||
int i;
|
||||
|
||||
/* check if we need to update */
|
||||
if(needs_update != 1)
|
||||
return;
|
||||
needs_update = 0;
|
||||
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
if(jobs_status(&master_servers[i].lookup.job) != JOBSTATUS_DONE)
|
||||
needs_update = 1;
|
||||
else
|
||||
{
|
||||
master_servers[i].addr = master_servers[i].lookup.addr;
|
||||
master_servers[i].addr.port = 8300;
|
||||
}
|
||||
}
|
||||
|
||||
if(!needs_update)
|
||||
{
|
||||
dbg_msg("engine/mastersrv", "saving addresses");
|
||||
mastersrv_save();
|
||||
}
|
||||
}
|
||||
|
||||
int mastersrv_refreshing()
|
||||
{
|
||||
return needs_update;
|
||||
}
|
||||
|
||||
NETADDR mastersrv_get(int index)
|
||||
{
|
||||
return master_servers[index].addr;
|
||||
}
|
||||
|
||||
const char *mastersrv_name(int index)
|
||||
{
|
||||
return master_servers[index].hostname;
|
||||
}
|
||||
|
||||
void mastersrv_dump_servers()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
dbg_msg("mastersrv", "#%d = %d.%d.%d.%d", i,
|
||||
master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
|
||||
master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void mastersrv_default()
|
||||
{
|
||||
int i;
|
||||
mem_zero(master_servers, sizeof(master_servers));
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
sprintf(master_servers[i].hostname, "master%d.teeworlds.com", i+1);
|
||||
}
|
||||
|
||||
int mastersrv_load()
|
||||
{
|
||||
LINEREADER lr;
|
||||
IOHANDLE file;
|
||||
int count = 0;
|
||||
|
||||
/* try to open file */
|
||||
file = engine_openfile("masters.cfg", IOFLAG_READ);
|
||||
if(!file)
|
||||
return -1;
|
||||
|
||||
linereader_init(&lr, file);
|
||||
while(1)
|
||||
{
|
||||
MASTER_INFO info = {{0}};
|
||||
int ip[4];
|
||||
const char *line = linereader_get(&lr);
|
||||
if(!line)
|
||||
break;
|
||||
|
||||
/* parse line */
|
||||
if(sscanf(line, "%s %d.%d.%d.%d", info.hostname, &ip[0], &ip[1], &ip[2], &ip[3]) == 5)
|
||||
{
|
||||
info.addr.ip[0] = (unsigned char)ip[0];
|
||||
info.addr.ip[1] = (unsigned char)ip[1];
|
||||
info.addr.ip[2] = (unsigned char)ip[2];
|
||||
info.addr.ip[3] = (unsigned char)ip[3];
|
||||
info.addr.port = 8300;
|
||||
if(count != MAX_MASTERSERVERS)
|
||||
{
|
||||
master_servers[count] = info;
|
||||
count++;
|
||||
}
|
||||
else
|
||||
dbg_msg("engine/mastersrv", "warning: skipped master server '%s' due to limit of %d", line, MAX_MASTERSERVERS);
|
||||
}
|
||||
else
|
||||
dbg_msg("engine/mastersrv", "warning: couldn't parse master server '%s'", line);
|
||||
}
|
||||
|
||||
io_close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mastersrv_save()
|
||||
{
|
||||
IOHANDLE file;
|
||||
int i;
|
||||
|
||||
/* try to open file */
|
||||
file = engine_openfile("masters.cfg", IOFLAG_WRITE);
|
||||
if(!file)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
char buf[1024];
|
||||
str_format(buf, sizeof(buf), "%s %d.%d.%d.%d\n", master_servers[i].hostname,
|
||||
master_servers[i].addr.ip[0], master_servers[i].addr.ip[1],
|
||||
master_servers[i].addr.ip[2], master_servers[i].addr.ip[3]);
|
||||
|
||||
io_write(file, buf, strlen(buf));
|
||||
}
|
||||
|
||||
io_close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostlookup_thread(void *user)
|
||||
{
|
||||
HOSTLOOKUP *lookup = (HOSTLOOKUP *)user;
|
||||
net_host_lookup(lookup->hostname, &lookup->addr, NETTYPE_IPV4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname)
|
||||
{
|
||||
str_copy(lookup->hostname, hostname, sizeof(lookup->hostname));
|
||||
jobs_add(&hostlookuppool, &lookup->job, hostlookup_thread, lookup);
|
||||
}
|
||||
|
||||
static int engine_find_datadir(char *argv0)
|
||||
{
|
||||
/* 1) use provided data-dir override */
|
||||
if(datadir[0])
|
||||
{
|
||||
if(fs_is_dir(datadir))
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
dbg_msg("engine/datadir", "specified data-dir '%s' does not exist", datadir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2) use data-dir in PWD if present */
|
||||
if(fs_is_dir("data"))
|
||||
{
|
||||
strcpy(datadir, "data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 3) use compiled-in data-dir if present */
|
||||
if (fs_is_dir(DATA_DIR))
|
||||
{
|
||||
strcpy(datadir, DATA_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 4) check for usable path in argv[0] */
|
||||
{
|
||||
unsigned int pos = strrchr(argv0, '/') - argv0;
|
||||
|
||||
if (pos < sizeof(datadir))
|
||||
{
|
||||
char basedir[sizeof(datadir)];
|
||||
strncpy(basedir, argv0, pos);
|
||||
basedir[pos] = '\0';
|
||||
str_format(datadir, sizeof(datadir), "%s/data", basedir);
|
||||
|
||||
if (fs_is_dir(datadir))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_UNIX)
|
||||
/* 5) check for all default locations */
|
||||
{
|
||||
const char *sdirs[] = {
|
||||
"/usr/share/teeworlds",
|
||||
"/usr/local/share/teeworlds"
|
||||
"/opt/teeworlds"
|
||||
};
|
||||
const int sdirs_count = sizeof(sdirs) / sizeof(sdirs[0]);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < sdirs_count; i++)
|
||||
{
|
||||
if (fs_is_dir(sdirs[i]))
|
||||
{
|
||||
strcpy(datadir, sdirs[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* no data-dir found */
|
||||
dbg_msg("engine/datadir", "warning no data directory found");
|
||||
return -1;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#include "e_jobs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
const char *engine_savepath(const char *filename, char *buffer, int max);
|
||||
void engine_init(const char *appname);
|
||||
void engine_parse_arguments(int argc, char **argv);
|
||||
|
||||
int engine_config_write_start();
|
||||
void engine_config_write_line(const char *line);
|
||||
void engine_config_write_stop();
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
LISTDIRTYPE_SAVE=1,
|
||||
LISTDIRTYPE_CURRENT=2,
|
||||
LISTDIRTYPE_DATA=4,
|
||||
LISTDIRTYPE_ALL = ~0
|
||||
};
|
||||
|
||||
void engine_listdir(int types, const char *path, FS_LISTDIR_CALLBACK cb, void *user);
|
||||
IOHANDLE engine_openfile(const char *filename, int flags);
|
||||
void engine_getpath(char *buffer, int buffer_size, const char *filename, int flags);
|
||||
|
||||
int engine_stress(float probability);
|
||||
|
||||
typedef struct HOSTLOOKUP
|
||||
{
|
||||
JOB job;
|
||||
char hostname[128];
|
||||
NETADDR addr;
|
||||
} HOSTLOOKUP;
|
||||
|
||||
void engine_hostlookup(HOSTLOOKUP *lookup, const char *hostname);
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_MASTERSERVERS=4
|
||||
};
|
||||
|
||||
void mastersrv_default();
|
||||
int mastersrv_load();
|
||||
int mastersrv_save();
|
||||
|
||||
int mastersrv_refresh_addresses();
|
||||
void mastersrv_update();
|
||||
int mastersrv_refreshing();
|
||||
void mastersrv_dump_servers();
|
||||
NETADDR mastersrv_get(int index);
|
||||
const char *mastersrv_name(int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,277 +0,0 @@
|
||||
#include <memory.h> /* memset */
|
||||
#include <string.h> /* memset */
|
||||
#include "e_huffman.h"
|
||||
|
||||
typedef struct HUFFMAN_CONSTRUCT_NODE
|
||||
{
|
||||
unsigned short node_id;
|
||||
int frequency;
|
||||
} HUFFMAN_CONSTRUCT_NODE;
|
||||
|
||||
static void huffman_setbits_r(HUFFMAN_STATE *huff, HUFFMAN_NODE *node, int bits, int depth)
|
||||
{
|
||||
if(node->leafs[1] != 0xffff)
|
||||
huffman_setbits_r(huff, &huff->nodes[node->leafs[1]], bits|(1<<depth), depth+1);
|
||||
if(node->leafs[0] != 0xffff)
|
||||
huffman_setbits_r(huff, &huff->nodes[node->leafs[0]], bits, depth+1);
|
||||
|
||||
if(node->num_bits)
|
||||
{
|
||||
node->bits = bits;
|
||||
node->num_bits = depth;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: this should be something faster, but it's enough for now */
|
||||
void bubblesort(HUFFMAN_CONSTRUCT_NODE **list, int size)
|
||||
{
|
||||
int i, changed = 1;
|
||||
HUFFMAN_CONSTRUCT_NODE *temp;
|
||||
|
||||
while(changed)
|
||||
{
|
||||
changed = 0;
|
||||
for(i = 0; i < size-1; i++)
|
||||
{
|
||||
if(list[i]->frequency < list[i+1]->frequency)
|
||||
{
|
||||
temp = list[i];
|
||||
list[i] = list[i+1];
|
||||
list[i+1] = temp;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void huffman_construct_tree(HUFFMAN_STATE *huff, const unsigned *frequencies)
|
||||
{
|
||||
HUFFMAN_CONSTRUCT_NODE nodes_left_storage[HUFFMAN_MAX_SYMBOLS];
|
||||
HUFFMAN_CONSTRUCT_NODE *nodes_left[HUFFMAN_MAX_SYMBOLS];
|
||||
int num_nodes_left = HUFFMAN_MAX_SYMBOLS;
|
||||
int i;
|
||||
|
||||
/* add the symbols */
|
||||
for(i = 0; i < HUFFMAN_MAX_SYMBOLS; i++)
|
||||
{
|
||||
huff->nodes[i].num_bits = -1;
|
||||
huff->nodes[i].symbol = i;
|
||||
huff->nodes[i].leafs[0] = -1;
|
||||
huff->nodes[i].leafs[1] = -1;
|
||||
|
||||
if(i == HUFFMAN_EOF_SYMBOL)
|
||||
nodes_left_storage[i].frequency = 1;
|
||||
else
|
||||
nodes_left_storage[i].frequency = frequencies[i];
|
||||
nodes_left_storage[i].node_id = i;
|
||||
nodes_left[i] = &nodes_left_storage[i];
|
||||
|
||||
}
|
||||
huff->num_nodes = HUFFMAN_MAX_SYMBOLS;
|
||||
|
||||
/* construct the table */
|
||||
while(num_nodes_left > 1)
|
||||
{
|
||||
/* we can't rely on stdlib's qsort for this, it can generate different results on different implementations */
|
||||
bubblesort(nodes_left, num_nodes_left);
|
||||
|
||||
huff->nodes[huff->num_nodes].num_bits = 0;
|
||||
huff->nodes[huff->num_nodes].leafs[0] = nodes_left[num_nodes_left-1]->node_id;
|
||||
huff->nodes[huff->num_nodes].leafs[1] = nodes_left[num_nodes_left-2]->node_id;
|
||||
nodes_left[num_nodes_left-2]->node_id = huff->num_nodes;
|
||||
nodes_left[num_nodes_left-2]->frequency = nodes_left[num_nodes_left-1]->frequency + nodes_left[num_nodes_left-2]->frequency;
|
||||
|
||||
huff->num_nodes++;
|
||||
num_nodes_left--;
|
||||
}
|
||||
|
||||
/* set start node */
|
||||
huff->start_node = &huff->nodes[huff->num_nodes-1];
|
||||
|
||||
/* build symbol bits */
|
||||
huffman_setbits_r(huff, huff->start_node, 0, 0);
|
||||
}
|
||||
|
||||
void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* make sure to cleanout every thing */
|
||||
memset(huff, 0, sizeof(HUFFMAN_STATE));
|
||||
|
||||
/* construct the tree */
|
||||
huffman_construct_tree(huff, frequencies);
|
||||
|
||||
/* build decode LUT */
|
||||
for(i = 0; i < HUFFMAN_LUTSIZE; i++)
|
||||
{
|
||||
unsigned bits = i;
|
||||
int k;
|
||||
HUFFMAN_NODE *node = huff->start_node;
|
||||
for(k = 0; k < HUFFMAN_LUTBITS; k++)
|
||||
{
|
||||
node = &huff->nodes[node->leafs[bits&1]];
|
||||
bits >>= 1;
|
||||
|
||||
if(!node)
|
||||
break;
|
||||
|
||||
if(node->num_bits)
|
||||
{
|
||||
huff->decode_lut[i] = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(k == HUFFMAN_LUTBITS)
|
||||
huff->decode_lut[i] = node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size)
|
||||
{
|
||||
/* this macro loads a symbol for a byte into bits and bitcount */
|
||||
#define HUFFMAN_MACRO_LOADSYMBOL(sym) \
|
||||
bits |= huff->nodes[sym].bits << bitcount; \
|
||||
bitcount += huff->nodes[sym].num_bits;
|
||||
|
||||
/* this macro writes the symbol stored in bits and bitcount to the dst pointer */
|
||||
#define HUFFMAN_MACRO_WRITE() \
|
||||
while(bitcount >= 8) \
|
||||
{ \
|
||||
*dst++ = (unsigned char)(bits&0xff); \
|
||||
if(dst == dst_end) \
|
||||
return -1; \
|
||||
bits >>= 8; \
|
||||
bitcount -= 8; \
|
||||
}
|
||||
|
||||
/* setup buffer pointers */
|
||||
const unsigned char *src = (const unsigned char *)input;
|
||||
const unsigned char *src_end = src + input_size;
|
||||
unsigned char *dst = (unsigned char *)output;
|
||||
unsigned char *dst_end = dst + output_size;
|
||||
|
||||
/* symbol variables */
|
||||
unsigned bits = 0;
|
||||
unsigned bitcount = 0;
|
||||
|
||||
/* make sure that we have data that we want to compress */
|
||||
if(input_size)
|
||||
{
|
||||
/* {A} load the first symbol */
|
||||
int symbol = *src++;
|
||||
|
||||
while(src != src_end)
|
||||
{
|
||||
/* {B} load the symbol */
|
||||
HUFFMAN_MACRO_LOADSYMBOL(symbol)
|
||||
|
||||
/* {C} fetch next symbol, this is done here because it will reduce dependency in the code */
|
||||
symbol = *src++;
|
||||
|
||||
/* {B} write the symbol loaded at */
|
||||
HUFFMAN_MACRO_WRITE()
|
||||
}
|
||||
|
||||
/* write the last symbol loaded from {C} or {A} in the case of only 1 byte input buffer */
|
||||
HUFFMAN_MACRO_LOADSYMBOL(symbol)
|
||||
HUFFMAN_MACRO_WRITE()
|
||||
}
|
||||
|
||||
/* write EOF symbol */
|
||||
HUFFMAN_MACRO_LOADSYMBOL(HUFFMAN_EOF_SYMBOL)
|
||||
HUFFMAN_MACRO_WRITE()
|
||||
|
||||
/* write out the last bits */
|
||||
*dst++ = bits;
|
||||
|
||||
/* return the size of the output */
|
||||
return (int)(dst - (const unsigned char *)output);
|
||||
|
||||
/* remove macros */
|
||||
#undef HUFFMAN_MACRO_LOADSYMBOL
|
||||
#undef HUFFMAN_MACRO_WRITE
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size)
|
||||
{
|
||||
/* setup buffer pointers */
|
||||
unsigned char *dst = (unsigned char *)output;
|
||||
unsigned char *src = (unsigned char *)input;
|
||||
unsigned char *dst_end = dst + output_size;
|
||||
unsigned char *src_end = src + input_size;
|
||||
|
||||
unsigned bits = 0;
|
||||
unsigned bitcount = 0;
|
||||
|
||||
HUFFMAN_NODE *eof = &huff->nodes[HUFFMAN_EOF_SYMBOL];
|
||||
HUFFMAN_NODE *node = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* {A} try to load a node now, this will reduce dependency at location {D} */
|
||||
node = 0;
|
||||
if(bitcount >= HUFFMAN_LUTBITS)
|
||||
node = huff->decode_lut[bits&HUFFMAN_LUTMASK];
|
||||
|
||||
/* {B} fill with new bits */
|
||||
while(bitcount < 24 && src != src_end)
|
||||
{
|
||||
bits |= (*src++) << bitcount;
|
||||
bitcount += 8;
|
||||
}
|
||||
|
||||
/* {C} load symbol now if we didn't that earlier at location {A} */
|
||||
if(!node)
|
||||
node = huff->decode_lut[bits&HUFFMAN_LUTMASK];
|
||||
|
||||
/* {D} check if we hit a symbol already */
|
||||
if(node->num_bits)
|
||||
{
|
||||
/* remove the bits for that symbol */
|
||||
bits >>= node->num_bits;
|
||||
bitcount -= node->num_bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* remove the bits that the lut checked up for us */
|
||||
bits >>= HUFFMAN_LUTBITS;
|
||||
bitcount -= HUFFMAN_LUTBITS;
|
||||
|
||||
/* walk the tree bit by bit */
|
||||
while(1)
|
||||
{
|
||||
/* traverse tree */
|
||||
node = &huff->nodes[node->leafs[bits&1]];
|
||||
|
||||
/* remove bit */
|
||||
bitcount--;
|
||||
bits >>= 1;
|
||||
|
||||
/* check if we hit a symbol */
|
||||
if(node->num_bits)
|
||||
break;
|
||||
|
||||
/* no more bits, decoding error */
|
||||
if(bitcount == 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for eof */
|
||||
if(node == eof)
|
||||
break;
|
||||
|
||||
/* output character */
|
||||
if(dst == dst_end)
|
||||
return -1;
|
||||
*dst++ = node->symbol;
|
||||
}
|
||||
|
||||
/* return the size of the decompressed buffer */
|
||||
return (int)(dst - (const unsigned char *)output);
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#ifndef __HUFFMAN_HEADER__
|
||||
#define __HUFFMAN_HEADER__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
HUFFMAN_EOF_SYMBOL = 256,
|
||||
|
||||
HUFFMAN_MAX_SYMBOLS=HUFFMAN_EOF_SYMBOL+1,
|
||||
HUFFMAN_MAX_NODES=HUFFMAN_MAX_SYMBOLS*2-1,
|
||||
|
||||
HUFFMAN_LUTBITS = 10,
|
||||
HUFFMAN_LUTSIZE = (1<<HUFFMAN_LUTBITS),
|
||||
HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE-1)
|
||||
};
|
||||
|
||||
typedef struct HUFFMAN_NODE
|
||||
{
|
||||
/* symbol */
|
||||
unsigned bits;
|
||||
unsigned num_bits;
|
||||
|
||||
/* don't use pointers for this. shorts are smaller so we can fit more data into the cache */
|
||||
unsigned short leafs[2];
|
||||
|
||||
/* what the symbol represents */
|
||||
unsigned char symbol;
|
||||
} HUFFMAN_NODE;
|
||||
|
||||
typedef struct HUFFMAN_STATE
|
||||
{
|
||||
HUFFMAN_NODE nodes[HUFFMAN_MAX_NODES];
|
||||
HUFFMAN_NODE *decode_lut[HUFFMAN_LUTSIZE];
|
||||
HUFFMAN_NODE *start_node;
|
||||
int num_nodes;
|
||||
} HUFFMAN_STATE;
|
||||
|
||||
/*
|
||||
Function: huffman_init
|
||||
Inits the compressor/decompressor.
|
||||
|
||||
Parameters:
|
||||
huff - Pointer to the state to init
|
||||
frequencies - A pointer to an array of 256 entries of the frequencies of the bytes
|
||||
|
||||
Remarks:
|
||||
- Does no allocation what so ever.
|
||||
- You don't have to call any cleanup functions when you are done with it
|
||||
*/
|
||||
void huffman_init(HUFFMAN_STATE *huff, const unsigned *frequencies);
|
||||
|
||||
/*
|
||||
Function: huffman_compress
|
||||
Compresses a buffer and outputs a compressed buffer.
|
||||
|
||||
Parameters:
|
||||
huff - Pointer to the huffman state
|
||||
input - Buffer to compress
|
||||
input_size - Size of the buffer to compress
|
||||
output - Buffer to put the compressed data into
|
||||
output_size - Size of the output buffer
|
||||
|
||||
Returns:
|
||||
Returns the size of the compressed data. Negative value on failure.
|
||||
*/
|
||||
int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size);
|
||||
|
||||
/*
|
||||
Function: huffman_decompress
|
||||
Decompresses a buffer
|
||||
|
||||
Parameters:
|
||||
huff - Pointer to the huffman state
|
||||
input - Buffer to decompress
|
||||
input_size - Size of the buffer to decompress
|
||||
output - Buffer to put the uncompressed data into
|
||||
output_size - Size of the output buffer
|
||||
|
||||
Returns:
|
||||
Returns the size of the uncompressed data. Negative value on failure.
|
||||
*/
|
||||
int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HUFFMAN_HEADER__ */
|
||||
@@ -1,579 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_CLIENT_H
|
||||
#define ENGINE_IF_CLIENT_H
|
||||
|
||||
/*
|
||||
Title: Client Interface
|
||||
*/
|
||||
|
||||
/*
|
||||
Section: Constants
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
/* Constants: Client States
|
||||
CLIENTSTATE_OFFLINE - The client is offline.
|
||||
CLIENTSTATE_CONNECTING - The client is trying to connect to a server.
|
||||
CLIENTSTATE_LOADING - The client has connected to a server and is loading resources.
|
||||
CLIENTSTATE_ONLINE - The client is connected to a server and running the game.
|
||||
CLIENTSTATE_DEMOPLAYBACK - The client is playing a demo
|
||||
CLIENTSTATE_QUITING - The client is quiting.
|
||||
*/
|
||||
CLIENTSTATE_OFFLINE=0,
|
||||
CLIENTSTATE_CONNECTING,
|
||||
CLIENTSTATE_LOADING,
|
||||
CLIENTSTATE_ONLINE,
|
||||
CLIENTSTATE_DEMOPLAYBACK,
|
||||
CLIENTSTATE_QUITING,
|
||||
|
||||
/* Constants: Image Formats
|
||||
IMG_AUTO - Lets the engine choose the format.
|
||||
IMG_RGB - 8-Bit uncompressed RGB
|
||||
IMG_RGBA - 8-Bit uncompressed RGBA
|
||||
IMG_ALPHA - 8-Bit uncompressed alpha
|
||||
*/
|
||||
IMG_AUTO=-1,
|
||||
IMG_RGB=0,
|
||||
IMG_RGBA=1,
|
||||
IMG_ALPHA=2,
|
||||
|
||||
/* Constants: Texture Loading Flags
|
||||
TEXLOAD_NORESAMPLE - Prevents the texture from any resampling
|
||||
*/
|
||||
TEXLOAD_NORESAMPLE=1,
|
||||
|
||||
/* Constants: Server Browser Sorting
|
||||
BROWSESORT_NAME - Sort by name.
|
||||
BROWSESORT_PING - Sort by ping.
|
||||
BROWSESORT_MAP - Sort by map
|
||||
BROWSESORT_GAMETYPE - Sort by game type. DM, TDM etc.
|
||||
BROWSESORT_PROGRESSION - Sort by progression.
|
||||
BROWSESORT_NUMPLAYERS - Sort after how many players there are on the server.
|
||||
*/
|
||||
BROWSESORT_NAME = 0,
|
||||
BROWSESORT_PING,
|
||||
BROWSESORT_MAP,
|
||||
BROWSESORT_GAMETYPE,
|
||||
BROWSESORT_PROGRESSION,
|
||||
BROWSESORT_NUMPLAYERS,
|
||||
|
||||
BROWSEQUICK_SERVERNAME=1,
|
||||
BROWSEQUICK_PLAYERNAME=2,
|
||||
BROWSEQUICK_MAPNAME=4,
|
||||
|
||||
BROWSETYPE_INTERNET = 0,
|
||||
BROWSETYPE_LAN = 1,
|
||||
BROWSETYPE_FAVORITES = 2
|
||||
};
|
||||
|
||||
/*
|
||||
Section: Structures
|
||||
*/
|
||||
|
||||
/*
|
||||
Structure: SERVER_INFO_PLAYER
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char name[48];
|
||||
int score;
|
||||
} SERVER_INFO_PLAYER;
|
||||
|
||||
/*
|
||||
Structure: SERVER_INFO
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int sorted_index;
|
||||
int server_index;
|
||||
|
||||
NETADDR netaddr;
|
||||
|
||||
int quicksearch_hit;
|
||||
|
||||
int progression;
|
||||
int max_players;
|
||||
int num_players;
|
||||
int flags;
|
||||
int favorite;
|
||||
int latency; /* in ms */
|
||||
char gametype[16];
|
||||
char name[64];
|
||||
char map[32];
|
||||
char version[32];
|
||||
char address[24];
|
||||
SERVER_INFO_PLAYER players[16];
|
||||
} SERVER_INFO;
|
||||
|
||||
/*
|
||||
Section: Functions
|
||||
*/
|
||||
|
||||
/**********************************************************************************
|
||||
Group: Time
|
||||
**********************************************************************************/
|
||||
/*
|
||||
Function: client_tick
|
||||
Returns the tick of the current snapshot.
|
||||
*/
|
||||
int client_tick();
|
||||
|
||||
/*
|
||||
Function: client_prevtick
|
||||
Returns the tick of the previous snapshot.
|
||||
*/
|
||||
int client_prevtick();
|
||||
|
||||
/*
|
||||
Function: client_intratick
|
||||
Returns the current intratick.
|
||||
|
||||
Remarks:
|
||||
The intratick is how far gone the time is from the previous snapshot to the current.
|
||||
0.0 means that it on the previous snapshot. 0.5 means that it's halfway to the current,
|
||||
and 1.0 means that it is on the current snapshot. It can go beyond 1.0 which means that
|
||||
the client has started to extrapolate due to lack of data from the server.
|
||||
|
||||
See Also:
|
||||
<client_tick>
|
||||
*/
|
||||
float client_intratick();
|
||||
|
||||
/*
|
||||
Function: client_predtick
|
||||
Returns the current predicted tick.
|
||||
*/
|
||||
int client_predtick();
|
||||
|
||||
/*
|
||||
Function: client_predintratick
|
||||
Returns the current preticted intra tick.
|
||||
|
||||
Remarks:
|
||||
This is the same as <client_intratick> but for the current predicted tick and
|
||||
previous predicted tick.
|
||||
|
||||
See Also:
|
||||
<client_intratick>
|
||||
*/
|
||||
float client_predintratick();
|
||||
|
||||
/*
|
||||
Function: client_ticktime
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
float client_ticktime();
|
||||
|
||||
/*
|
||||
Function: client_tickspeed
|
||||
Returns how many ticks per second the client is doing.
|
||||
|
||||
Remarks:
|
||||
This will be the same as the server tick speed.
|
||||
*/
|
||||
int client_tickspeed();
|
||||
|
||||
/*
|
||||
Function: client_frametime
|
||||
Returns how long time the last frame took to process.
|
||||
*/
|
||||
float client_frametime();
|
||||
|
||||
/*
|
||||
Function: client_localtime
|
||||
Returns the clients local time.
|
||||
|
||||
Remarks:
|
||||
The local time is set to 0 when the client starts and when
|
||||
it connects to a server. Can be used for client side effects.
|
||||
*/
|
||||
float client_localtime();
|
||||
|
||||
/**********************************************************************************
|
||||
Group: Server Browser
|
||||
**********************************************************************************/
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_refresh
|
||||
Issues a refresh of the server browser.
|
||||
|
||||
Arguments:
|
||||
type - What type of servers to browse, internet, lan or favorites.
|
||||
|
||||
Remarks:
|
||||
This will cause a broadcast on the local network if the lan argument is set.
|
||||
Otherwise it call ask all the master servers for their servers lists.
|
||||
*/
|
||||
void client_serverbrowse_refresh(int type);
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_sorted_get
|
||||
Returns server info from the sorted list.
|
||||
|
||||
Arguments:
|
||||
index - Zero based index into the sorted list.
|
||||
|
||||
See Also:
|
||||
<client_serverbrowse_sorted_num>
|
||||
*/
|
||||
SERVER_INFO *client_serverbrowse_sorted_get(int index);
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_sorted_num
|
||||
Returns how many servers there are in the sorted list.
|
||||
|
||||
See Also:
|
||||
<client_serverbrowse_sorted_get>
|
||||
*/
|
||||
int client_serverbrowse_sorted_num();
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_get
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
SERVER_INFO *client_serverbrowse_get(int index);
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_num
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_serverbrowse_num();
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_num_requests
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_serverbrowse_num_requests();
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_update
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void client_serverbrowse_update();
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_lan
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_serverbrowse_lan();
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_isfavorite
|
||||
Asks the server browser is a netaddr is in the favorite list
|
||||
|
||||
Arguments:
|
||||
addr - Address of the server to ask about.
|
||||
|
||||
Returns:
|
||||
Returns zero if it's not in the list, non-zero if it is.
|
||||
*/
|
||||
int client_serverbrowse_isfavorite(NETADDR addr);
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_addfavorite
|
||||
Adds a server to the favorite list
|
||||
|
||||
Arguments:
|
||||
addr - Address of the favorite server.
|
||||
*/
|
||||
void client_serverbrowse_addfavorite(NETADDR addr);
|
||||
|
||||
/*
|
||||
Function: client_serverbrowse_removefavorite
|
||||
Removes a server to the favorite list
|
||||
|
||||
Arguments:
|
||||
addr - Address of the favorite server.
|
||||
*/
|
||||
void client_serverbrowse_removefavorite(NETADDR addr);
|
||||
|
||||
/**********************************************************************************
|
||||
Group: Actions
|
||||
**********************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
Function: client_connect
|
||||
Connects to a server at the specified address.
|
||||
|
||||
Arguments:
|
||||
address - Address of the server to connect to.
|
||||
|
||||
See Also:
|
||||
<client_disconnect>
|
||||
*/
|
||||
void client_connect(const char *address);
|
||||
|
||||
/*
|
||||
Function: client_disconnect
|
||||
Disconnects from the current server.
|
||||
|
||||
Remarks:
|
||||
Does nothing if not connected to a server.
|
||||
|
||||
See Also:
|
||||
<client_connect, client_quit>
|
||||
*/
|
||||
void client_disconnect();
|
||||
|
||||
/*
|
||||
Function: client_quit
|
||||
Tells to client to shutdown.
|
||||
|
||||
See Also:
|
||||
<client_disconnect>
|
||||
*/
|
||||
void client_quit();
|
||||
|
||||
void client_entergame();
|
||||
|
||||
/*
|
||||
Function: client_rcon
|
||||
Sends a command to the server to execute on it's console.
|
||||
|
||||
Arguments:
|
||||
cmd - The command to send.
|
||||
|
||||
Remarks:
|
||||
The client must have the correct rcon password to connect.
|
||||
|
||||
See Also:
|
||||
<client_rcon_auth, client_rcon_authed>
|
||||
*/
|
||||
void client_rcon(const char *cmd);
|
||||
|
||||
/*
|
||||
Function: client_rcon_auth
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<client_rcon, client_rcon_authed>
|
||||
*/
|
||||
void client_rcon_auth(const char *name, const char *password);
|
||||
|
||||
/*
|
||||
Function: client_rcon_authed
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<client_rcon, client_rcon_auth>
|
||||
*/
|
||||
int client_rcon_authed();
|
||||
|
||||
/**********************************************************************************
|
||||
Group: Other
|
||||
**********************************************************************************/
|
||||
/*
|
||||
Function: client_latestversion
|
||||
Returns 0 if there's no version difference
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *client_latestversion();
|
||||
|
||||
/*
|
||||
Function: client_get_input
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int *client_get_input(int tick);
|
||||
|
||||
/*
|
||||
Function: client_direct_input
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void client_direct_input(int *input, int size);
|
||||
|
||||
/*
|
||||
Function: client_error_string
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *client_error_string();
|
||||
|
||||
/*
|
||||
Function: client_connection_problems
|
||||
Returns 1 if the client is connection problems.
|
||||
|
||||
Remarks:
|
||||
Connections problems usually means that the client havn't recvived any data
|
||||
from the server in a while.
|
||||
*/
|
||||
int client_connection_problems();
|
||||
|
||||
/*
|
||||
Function: client_state
|
||||
Returns the state of the client.
|
||||
|
||||
See Also:
|
||||
<Client States>
|
||||
*/
|
||||
int client_state();
|
||||
|
||||
/*
|
||||
Function: client_mapdownload_amount
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_mapdownload_amount();
|
||||
|
||||
/*
|
||||
Function: client_mapdownload_totalsize
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_mapdownload_totalsize();
|
||||
|
||||
/*
|
||||
Function: client_save_line
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void client_save_line(const char *line);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
BROWSESET_MASTER_ADD=1,
|
||||
BROWSESET_FAV_ADD,
|
||||
BROWSESET_TOKEN,
|
||||
BROWSESET_OLD_INTERNET,
|
||||
BROWSESET_OLD_LAN
|
||||
};
|
||||
|
||||
void client_serverbrowse_set(NETADDR *addr, int type, int token, SERVER_INFO *info);
|
||||
|
||||
int client_serverbrowse_refreshingmasters();
|
||||
|
||||
|
||||
typedef struct DEMOPLAYBACK_INFO
|
||||
{
|
||||
int first_tick;
|
||||
int last_tick;
|
||||
int current_tick;
|
||||
int paused;
|
||||
float speed;
|
||||
} DEMOPLAYBACK_INFO;
|
||||
|
||||
void client_demoplayer_play(const char *filename);
|
||||
const DEMOPLAYBACK_INFO *client_demoplayer_getinfo();
|
||||
void client_demoplayer_setpos(float percent);
|
||||
void client_demoplayer_setpause(int paused);
|
||||
void client_demoplayer_setspeed(float speed);
|
||||
const char *client_user_directory();
|
||||
void client_serverinfo(SERVER_INFO *serverinfo);
|
||||
void client_serverinfo_request();
|
||||
void client_serverbrowse_request(NETADDR *addr);
|
||||
#endif
|
||||
@@ -1,673 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_GFX_H
|
||||
#define ENGINE_IF_GFX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
Title: Graphics
|
||||
*/
|
||||
|
||||
/*
|
||||
Section: Structures
|
||||
*/
|
||||
|
||||
/*
|
||||
Structure: IMAGE_INFO
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* Variable: width
|
||||
Contains the width of the image */
|
||||
int width;
|
||||
|
||||
/* Variable: height
|
||||
Contains the height of the image */
|
||||
int height;
|
||||
|
||||
/* Variable: format
|
||||
Contains the format of the image. See <Image Formats> for more information. */
|
||||
int format;
|
||||
|
||||
/* Variable: data
|
||||
Pointer to the image data. */
|
||||
void *data;
|
||||
} IMAGE_INFO;
|
||||
|
||||
/*
|
||||
Structure: VIDEO_MODE
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int width, height;
|
||||
int red, green, blue;
|
||||
} VIDEO_MODE;
|
||||
|
||||
/*
|
||||
Section: Functions
|
||||
*/
|
||||
|
||||
/*
|
||||
Group: Quads
|
||||
*/
|
||||
|
||||
/*
|
||||
Function: gfx_quads_begin
|
||||
Begins a quad drawing session.
|
||||
|
||||
Remarks:
|
||||
This functions resets the rotation, color and subset.
|
||||
End the session by using <gfx_quads_end>.
|
||||
You can't change texture or blending mode during a session.
|
||||
|
||||
See Also:
|
||||
<gfx_quads_end>
|
||||
*/
|
||||
void gfx_quads_begin();
|
||||
|
||||
/*
|
||||
Function: gfx_quads_end
|
||||
Ends a quad session.
|
||||
|
||||
See Also:
|
||||
<gfx_quads_begin>
|
||||
*/
|
||||
void gfx_quads_end();
|
||||
|
||||
/*
|
||||
Function: gfx_quads_setrotation
|
||||
Sets the rotation to use when drawing a quad.
|
||||
|
||||
Arguments:
|
||||
angle - Angle in radians.
|
||||
|
||||
Remarks:
|
||||
The angle is reset when <gfx_quads_begin> is called.
|
||||
*/
|
||||
void gfx_quads_setrotation(float angle);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_setsubset
|
||||
Sets the uv coordinates to use.
|
||||
|
||||
Arguments:
|
||||
tl_u - Top-left U value.
|
||||
tl_v - Top-left V value.
|
||||
br_u - Bottom-right U value.
|
||||
br_v - Bottom-right V value.
|
||||
|
||||
Remarks:
|
||||
O,0 is top-left of the texture and 1,1 is bottom-right.
|
||||
The color is reset when <gfx_quads_begin> is called.
|
||||
*/
|
||||
void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_setsubset_free
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_quads_setsubset_free(
|
||||
float x0, float y0,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_drawTL
|
||||
Draws a quad by specifying the top-left point.
|
||||
|
||||
Arguments:
|
||||
x - X coordinate of the top-left corner.
|
||||
y - Y coordinate of the top-left corner.
|
||||
width - Width of the quad.
|
||||
height - Height of the quad.
|
||||
|
||||
Remarks:
|
||||
Rotation still occurs from the center of the quad.
|
||||
You must call <gfx_quads_begin> before calling this function.
|
||||
|
||||
See Also:
|
||||
<gfx_quads_draw, gfx_quads_draw_freeform>
|
||||
*/
|
||||
void gfx_quads_drawTL(float x, float y, float width, float height);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_draw
|
||||
Draws a quad by specifying the center point.
|
||||
|
||||
Arguments:
|
||||
x - X coordinate of the center.
|
||||
y - Y coordinate of the center.
|
||||
width - Width of the quad.
|
||||
height - Height of the quad.
|
||||
|
||||
Remarks:
|
||||
You must call <gfx_quads_begin> before calling this function.
|
||||
|
||||
See Also:
|
||||
<gfx_quads_drawTL, gfx_quads_draw_freeform>
|
||||
*/
|
||||
void gfx_quads_draw(float x, float y, float w, float h);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_draw_freeform
|
||||
Draws a quad by specifying the corner points.
|
||||
|
||||
Arguments:
|
||||
x0, y0 - Coordinates of the upper left corner.
|
||||
x1, y1 - Coordinates of the upper right corner.
|
||||
x2, y2 - Coordinates of the lower left corner. // TODO: DOUBLE CHECK!!!
|
||||
x3, y3 - Coordinates of the lower right corner. // TODO: DOUBLE CHECK!!!
|
||||
|
||||
See Also:
|
||||
<gfx_quads_draw, gfx_quads_drawTL>
|
||||
*/
|
||||
void gfx_quads_draw_freeform(
|
||||
float x0, float y0,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3);
|
||||
|
||||
/*
|
||||
Function: gfx_quads_text
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text);
|
||||
|
||||
/*
|
||||
Group: Lines
|
||||
*/
|
||||
|
||||
/*
|
||||
Function: gfx_lines_begin
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_lines_begin();
|
||||
|
||||
/*
|
||||
Function: gfx_lines_draw
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_lines_draw(float x0, float y0, float x1, float y1);
|
||||
|
||||
/*
|
||||
Function: gfx_minimize
|
||||
Minimizes the window.
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_minimize();
|
||||
|
||||
/*
|
||||
Function: gfx_minimize
|
||||
Maximizes the window.
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_maximize();
|
||||
|
||||
/*
|
||||
Function: gfx_lines_end
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_lines_end();
|
||||
|
||||
/*
|
||||
Group: Text
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Function: gfx_text
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
returns the number of lines written
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_text(void *font, float x, float y, float size, const char *text, int max_width);
|
||||
|
||||
/*
|
||||
Function: gfx_text_width
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
float gfx_text_width(void *font, float size, const char *text, int length);
|
||||
|
||||
/*
|
||||
Function: gfx_text_color
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_text_color(float r, float g, float b, float a);
|
||||
|
||||
/*
|
||||
Function: gfx_text_set_default_font
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_text_set_default_font(void *font);
|
||||
|
||||
/*
|
||||
Group: Other
|
||||
*/
|
||||
|
||||
/*
|
||||
Function: gfx_get_video_modes
|
||||
Fetches a list of all the available video modes.
|
||||
|
||||
Arguments:
|
||||
list - An array to recive the modes. Must be able to contain maxcount number of modes.
|
||||
maxcount - The maximum number of modes to fetch.
|
||||
|
||||
Returns:
|
||||
The number of video modes that was fetched.
|
||||
*/
|
||||
int gfx_get_video_modes(VIDEO_MODE *list, int maxcount);
|
||||
|
||||
/* image loaders */
|
||||
|
||||
/*
|
||||
Function: gfx_load_png
|
||||
Loads a PNG image from disk.
|
||||
|
||||
Arguments:
|
||||
img - Pointer to an structure to be filled out with the image information.
|
||||
filename - Filename of the image to load.
|
||||
|
||||
Returns:
|
||||
Returns non-zero on success and zero on error.
|
||||
|
||||
Remarks:
|
||||
The caller are responsible for cleaning up the allocated memory in the IMAGE_INFO structure.
|
||||
|
||||
See Also:
|
||||
<gfx_load_texture>
|
||||
*/
|
||||
int gfx_load_png(IMAGE_INFO *img, const char *filename);
|
||||
|
||||
/* textures */
|
||||
/*
|
||||
Function: gfx_load_texture
|
||||
Loads a texture from a file. TGA and PNG supported.
|
||||
|
||||
Arguments:
|
||||
filename - Null terminated string to the file to load.
|
||||
store_format - What format to store on gfx card as.
|
||||
flags - controls how the texture is uploaded
|
||||
|
||||
Returns:
|
||||
An ID to the texture. -1 on failure.
|
||||
|
||||
See Also:
|
||||
<gfx_unload_texture, gfx_load_png>
|
||||
*/
|
||||
int gfx_load_texture(const char *filename, int store_format, int flags);
|
||||
|
||||
/*
|
||||
Function: gfx_load_texture_raw
|
||||
Loads a texture from memory.
|
||||
|
||||
Arguments:
|
||||
w - Width of the texture.
|
||||
h - Height of the texture.
|
||||
data - Pointer to the pixel data.
|
||||
format - Format of the pixel data.
|
||||
store_format - The format to store the texture on the graphics card.
|
||||
flags - controls how the texture is uploaded
|
||||
|
||||
Returns:
|
||||
An ID to the texture. -1 on failure.
|
||||
|
||||
Remarks:
|
||||
The pixel data should be in RGBA format with 8 bit per component.
|
||||
So the total size of the data should be w*h*4.
|
||||
|
||||
See Also:
|
||||
<gfx_unload_texture>
|
||||
*/
|
||||
int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format, int flags);
|
||||
|
||||
/*
|
||||
Function: gfx_texture_set
|
||||
Sets the active texture.
|
||||
|
||||
Arguments:
|
||||
id - ID to the texture to set.
|
||||
*/
|
||||
void gfx_texture_set(int id);
|
||||
|
||||
/*
|
||||
Function: gfx_unload_texture
|
||||
Unloads a texture.
|
||||
|
||||
Arguments:
|
||||
id - ID to the texture to unload.
|
||||
|
||||
See Also:
|
||||
<gfx_load_texture_tga>, <gfx_load_texture_raw>
|
||||
|
||||
Remarks:
|
||||
NOT IMPLEMENTED
|
||||
*/
|
||||
int gfx_unload_texture(int id);
|
||||
|
||||
/*
|
||||
Function: gfx_clear
|
||||
Clears the screen with the specified color.
|
||||
|
||||
Arguments:
|
||||
r - Red component.
|
||||
g - Green component.
|
||||
b - Red component.
|
||||
|
||||
Remarks:
|
||||
The value of the components are given in 0.0 - 1.0 ranges.
|
||||
*/
|
||||
void gfx_clear(float r, float g, float b);
|
||||
|
||||
/*
|
||||
Function: gfx_screenaspect
|
||||
Returns the aspect ratio between width and height.
|
||||
|
||||
See Also:
|
||||
<gfx_screenwidth>, <gfx_screenheight>
|
||||
*/
|
||||
float gfx_screenaspect();
|
||||
|
||||
/*
|
||||
Function: gfx_screenwidth
|
||||
Returns the screen width.
|
||||
|
||||
See Also:
|
||||
<gfx_screenheight>
|
||||
*/
|
||||
int gfx_screenwidth();
|
||||
|
||||
/*
|
||||
Function: gfx_screenheight
|
||||
Returns the screen height.
|
||||
|
||||
See Also:
|
||||
<gfx_screenwidth>
|
||||
*/
|
||||
int gfx_screenheight();
|
||||
|
||||
/*
|
||||
Function: gfx_mapscreen
|
||||
Specifies the coordinate system for the screen.
|
||||
|
||||
Arguments:
|
||||
tl_x - Top-left X
|
||||
tl_y - Top-left Y
|
||||
br_x - Bottom-right X
|
||||
br_y - Bottom-right y
|
||||
*/
|
||||
void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y);
|
||||
|
||||
/*
|
||||
Function: gfx_blend_normal
|
||||
Set the active blending mode to normal (src, 1-src).
|
||||
|
||||
Remarks:
|
||||
This must be used before calling <gfx_quads_begin>.
|
||||
This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
|
||||
|
||||
See Also:
|
||||
<gfx_blend_additive,gfx_blend_none>
|
||||
*/
|
||||
void gfx_blend_normal();
|
||||
|
||||
/*
|
||||
Function: gfx_blend_additive
|
||||
Set the active blending mode to additive (src, one).
|
||||
|
||||
Remarks:
|
||||
This must be used before calling <gfx_quads_begin>.
|
||||
This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE).
|
||||
|
||||
See Also:
|
||||
<gfx_blend_normal,gfx_blend_none>
|
||||
*/
|
||||
void gfx_blend_additive();
|
||||
|
||||
/*
|
||||
Function: gfx_blend_none
|
||||
Disables blending
|
||||
|
||||
Remarks:
|
||||
This must be used before calling <gfx_quads_begin>.
|
||||
|
||||
See Also:
|
||||
<gfx_blend_normal,gfx_blend_additive>
|
||||
*/
|
||||
void gfx_blend_none();
|
||||
|
||||
|
||||
/*
|
||||
Function: gfx_setcolorvertex
|
||||
Sets the color of a vertex.
|
||||
|
||||
Arguments:
|
||||
i - Index to the vertex.
|
||||
r - Red value.
|
||||
g - Green value.
|
||||
b - Blue value.
|
||||
a - Alpha value.
|
||||
|
||||
Remarks:
|
||||
The color values are from 0.0 to 1.0.
|
||||
The color is reset when <gfx_quads_begin> is called.
|
||||
*/
|
||||
void gfx_setcolorvertex(int i, float r, float g, float b, float a);
|
||||
|
||||
/*
|
||||
Function: gfx_setcolor
|
||||
Sets the color of all the vertices.
|
||||
|
||||
Arguments:
|
||||
r - Red value.
|
||||
g - Green value.
|
||||
b - Blue value.
|
||||
a - Alpha value.
|
||||
|
||||
Remarks:
|
||||
The color values are from 0.0 to 1.0.
|
||||
The color is reset when <gfx_quads_begin> is called.
|
||||
*/
|
||||
void gfx_setcolor(float r, float g, float b, float a);
|
||||
|
||||
/*
|
||||
Function: gfx_getscreen
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y);
|
||||
|
||||
/*
|
||||
Function: gfx_memory_usage
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int gfx_memory_usage();
|
||||
|
||||
/*
|
||||
Function: gfx_screenshot
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
filename - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_screenshot();
|
||||
|
||||
/*
|
||||
Function: gfx_screenshot_direct
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
filename - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_screenshot_direct(const char *filename);
|
||||
|
||||
/*
|
||||
Function: gfx_clip_enable
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_clip_enable(int x, int y, int w, int h);
|
||||
|
||||
/*
|
||||
Function: gfx_clip_disable
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void gfx_clip_disable();
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
TEXTFLAG_RENDER=1,
|
||||
TEXTFLAG_ALLOW_NEWLINE=2,
|
||||
TEXTFLAG_STOP_AT_END=4
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int flags;
|
||||
int line_count;
|
||||
int charcount;
|
||||
|
||||
float start_x;
|
||||
float start_y;
|
||||
float line_width;
|
||||
float x, y;
|
||||
void *font_set;
|
||||
float font_size;
|
||||
} TEXT_CURSOR;
|
||||
|
||||
void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags);
|
||||
void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,255 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_INP_H
|
||||
#define ENGINE_IF_INP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
Section: Input
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Structure: INPUT_EVENT
|
||||
*/
|
||||
enum
|
||||
{
|
||||
INPFLAG_PRESS=1,
|
||||
INPFLAG_RELEASE=2,
|
||||
INPFLAG_REPEAT=4
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int flags;
|
||||
unsigned char ch;
|
||||
int key;
|
||||
} INPUT_EVENT;
|
||||
|
||||
/*
|
||||
Function: inp_mouse_relative
|
||||
Fetches the mouse movements.
|
||||
|
||||
Arguments:
|
||||
x - Pointer to the variable that should get the X movement.
|
||||
y - Pointer to the variable that should get the Y movement.
|
||||
*/
|
||||
void inp_mouse_relative(int *x, int *y);
|
||||
|
||||
void inp_mouse_absolute(int *x, int *y);
|
||||
|
||||
/*
|
||||
Function: inp_mouse_scroll
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_mouse_scroll();
|
||||
|
||||
/*
|
||||
Function: inp_key_pressed
|
||||
Checks if a key is pressed.
|
||||
|
||||
Arguments:
|
||||
key - Index to the key to check
|
||||
|
||||
Returns:
|
||||
Returns 1 if the button is pressed, otherwise 0.
|
||||
|
||||
Remarks:
|
||||
Check keys.h for the keys.
|
||||
*/
|
||||
int inp_key_pressed(int key);
|
||||
|
||||
|
||||
/* input */
|
||||
/*
|
||||
Function: inp_key_was_pressed
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_was_pressed(int key);
|
||||
|
||||
/*
|
||||
Function: inp_key_down
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_down(int key);
|
||||
|
||||
|
||||
/*
|
||||
Function: inp_num_events
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_num_events();
|
||||
|
||||
/*
|
||||
Function: inp_get_event
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
INPUT_EVENT inp_get_event(int index);
|
||||
|
||||
/*
|
||||
Function: inp_clear_events
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void inp_clear_events();
|
||||
|
||||
/*
|
||||
Function: inp_mouse_doubleclick
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_mouse_doubleclick();
|
||||
|
||||
/*
|
||||
Function: inp_key_presses
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_presses(int key);
|
||||
|
||||
/*
|
||||
Function: inp_key_releases
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_releases(int key);
|
||||
|
||||
/*
|
||||
Function: inp_key_state
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_state(int key);
|
||||
|
||||
/*
|
||||
Function: inp_key_name
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *inp_key_name(int k);
|
||||
|
||||
/*
|
||||
Function: inp_key_code
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int inp_key_code(const char *key_name);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Function: inp_clear_key_states
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void inp_clear_key_states();
|
||||
|
||||
void inp_update();
|
||||
void inp_init();
|
||||
void inp_mouse_mode_absolute();
|
||||
void inp_mouse_mode_relative();
|
||||
void inp_warp_mouse(int x, int y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,153 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_MODC_H
|
||||
#define ENGINE_IF_MODC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**********************************************************************************
|
||||
Section: Client Hooks
|
||||
*********************************************************************************/
|
||||
/*
|
||||
Function: modc_console_init
|
||||
TODO
|
||||
*/
|
||||
void modc_console_init();
|
||||
|
||||
/*
|
||||
Function: modc_rcon_line
|
||||
TODO
|
||||
*/
|
||||
void modc_rcon_line(const char *line);
|
||||
|
||||
/*
|
||||
Function: modc_save_config
|
||||
TODO
|
||||
*/
|
||||
void modc_save_config();
|
||||
|
||||
/*
|
||||
Function: modc_init
|
||||
Called when the client starts.
|
||||
|
||||
Remarks:
|
||||
The game should load resources that are used during the entire
|
||||
time of the game. No map is loaded.
|
||||
*/
|
||||
void modc_init();
|
||||
|
||||
/*
|
||||
Function: modc_newsnapshot
|
||||
Called when the client progressed to a new snapshot.
|
||||
|
||||
Remarks:
|
||||
The client can check for items in the snapshot and perform one time
|
||||
events like playing sounds, spawning client side effects etc.
|
||||
*/
|
||||
void modc_newsnapshot();
|
||||
|
||||
/*
|
||||
Function: modc_entergame
|
||||
Called when the client has successfully connect to a server and
|
||||
loaded a map.
|
||||
|
||||
Remarks:
|
||||
The client can check for items in the map and load them.
|
||||
*/
|
||||
void modc_entergame();
|
||||
|
||||
/*
|
||||
Function: modc_shutdown
|
||||
Called when the client closes down.
|
||||
*/
|
||||
void modc_shutdown();
|
||||
|
||||
/*
|
||||
Function: modc_render
|
||||
Called every frame to let the game render it self.
|
||||
*/
|
||||
void modc_render();
|
||||
|
||||
/*
|
||||
Function: modc_statechange
|
||||
Called every time client changes state.
|
||||
*/
|
||||
void modc_statechange(int new_state, int old_state);
|
||||
|
||||
/* undocumented callbacks */
|
||||
/*
|
||||
Function: modc_connected
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void modc_connected();
|
||||
|
||||
/*
|
||||
Function: modc_message
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void modc_message(int msg);
|
||||
|
||||
/*
|
||||
Function: modc_predict
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void modc_predict();
|
||||
|
||||
/*
|
||||
Function: modc_snap_input
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int modc_snap_input(int *data);
|
||||
|
||||
/*
|
||||
Function: modc_net_version
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *modc_net_version();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,176 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_MOD_H
|
||||
#define ENGINE_IF_MOD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**********************************************************************************
|
||||
Section: Server Hooks
|
||||
**********************************************************************************/
|
||||
/*
|
||||
Function: mods_console_init
|
||||
TODO
|
||||
*/
|
||||
void mods_console_init();
|
||||
|
||||
/*
|
||||
Function: mods_init
|
||||
Called when the server is started.
|
||||
|
||||
Remarks:
|
||||
It's called after the map is loaded so all map items are available.
|
||||
*/
|
||||
void mods_init();
|
||||
|
||||
/*
|
||||
Function: mods_shutdown
|
||||
Called when the server quits.
|
||||
|
||||
Remarks:
|
||||
Should be used to clean up all resources used.
|
||||
*/
|
||||
void mods_shutdown();
|
||||
|
||||
/*
|
||||
Function: mods_client_enter
|
||||
Called when a client has joined the game.
|
||||
|
||||
Arguments:
|
||||
cid - Client ID. Is 0 - MAX_CLIENTS.
|
||||
|
||||
Remarks:
|
||||
It's called when the client is finished loading and should enter gameplay.
|
||||
*/
|
||||
void mods_client_enter(int cid);
|
||||
|
||||
/*
|
||||
Function: mods_client_drop
|
||||
Called when a client drops from the server.
|
||||
|
||||
Arguments:
|
||||
cid - Client ID. Is 0 - MAX_CLIENTS
|
||||
*/
|
||||
void mods_client_drop(int cid);
|
||||
|
||||
/*
|
||||
Function: mods_client_direct_input
|
||||
Called when the server recives new input from a client.
|
||||
|
||||
Arguments:
|
||||
cid - Client ID. Is 0 - MAX_CLIENTS.
|
||||
input - Pointer to the input data.
|
||||
size - Size of the data. (NOT IMPLEMENTED YET)
|
||||
*/
|
||||
void mods_client_direct_input(int cid, void *input);
|
||||
|
||||
/*
|
||||
Function: mods_client_predicted_input
|
||||
Called when the server applys the predicted input on the client.
|
||||
|
||||
Arguments:
|
||||
cid - Client ID. Is 0 - MAX_CLIENTS.
|
||||
input - Pointer to the input data.
|
||||
size - Size of the data. (NOT IMPLEMENTED YET)
|
||||
*/
|
||||
void mods_client_predicted_input(int cid, void *input);
|
||||
|
||||
|
||||
/*
|
||||
Function: mods_tick
|
||||
Called with a regular interval to progress the gameplay.
|
||||
|
||||
Remarks:
|
||||
The SERVER_TICK_SPEED tells the number of ticks per second.
|
||||
*/
|
||||
void mods_tick();
|
||||
|
||||
/*
|
||||
Function: mods_presnap
|
||||
Called before the server starts to construct snapshots for the clients.
|
||||
*/
|
||||
void mods_presnap();
|
||||
|
||||
/*
|
||||
Function: mods_snap
|
||||
Called to create the snapshot for a client.
|
||||
|
||||
Arguments:
|
||||
cid - Client ID. Is 0 - MAX_CLIENTS.
|
||||
|
||||
Remarks:
|
||||
The game should make a series of calls to <snap_new_item> to construct
|
||||
the snapshot for the client.
|
||||
*/
|
||||
void mods_snap(int cid);
|
||||
|
||||
/*
|
||||
Function: mods_postsnap
|
||||
Called after the server is done sending the snapshots.
|
||||
*/
|
||||
void mods_postsnap();
|
||||
|
||||
|
||||
/*
|
||||
Function: mods_connected
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void mods_connected(int client_id);
|
||||
|
||||
|
||||
/*
|
||||
Function: mods_net_version
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *mods_net_version();
|
||||
|
||||
/*
|
||||
Function: mods_version
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *mods_version();
|
||||
|
||||
/*
|
||||
Function: mods_message
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void mods_message(int msg, int client_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,158 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_MSG_H
|
||||
#define ENGINE_IF_MSG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
Section: Messaging
|
||||
*/
|
||||
|
||||
void msg_pack_start_system(int msg, int flags);
|
||||
|
||||
/*
|
||||
Function: msg_pack_start
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void msg_pack_start(int msg, int flags);
|
||||
|
||||
/*
|
||||
Function: msg_pack_int
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void msg_pack_int(int i);
|
||||
|
||||
/*
|
||||
Function: msg_pack_string
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void msg_pack_string(const char *p, int limit);
|
||||
|
||||
/*
|
||||
Function: msg_pack_raw
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void msg_pack_raw(const void *data, int size);
|
||||
|
||||
/*
|
||||
Function: msg_pack_end
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void msg_pack_end();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int msg;
|
||||
int flags;
|
||||
const unsigned char *data;
|
||||
int size;
|
||||
} MSG_INFO;
|
||||
|
||||
const MSG_INFO *msg_get_info();
|
||||
|
||||
/* message unpacking */
|
||||
int msg_unpack_start(const void *data, int data_size, int *is_system);
|
||||
|
||||
/*
|
||||
Function: msg_unpack_int
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int msg_unpack_int();
|
||||
|
||||
/*
|
||||
Function: msg_unpack_string
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *msg_unpack_string();
|
||||
|
||||
/*
|
||||
Function: msg_unpack_raw
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const unsigned char *msg_unpack_raw(int size);
|
||||
|
||||
/*
|
||||
Function: msg_unpack_error
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int msg_unpack_error();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,392 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_OTHER_H
|
||||
#define ENGINE_IF_OTHER_H
|
||||
|
||||
/*
|
||||
Title: Engine Interface
|
||||
*/
|
||||
|
||||
#include <base/system.h>
|
||||
#include "e_keys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
SERVER_TICK_SPEED=50,
|
||||
MAX_CLIENTS=16,
|
||||
|
||||
SNAP_CURRENT=0,
|
||||
SNAP_PREV=1,
|
||||
|
||||
MASK_NONE=0,
|
||||
MASK_SET,
|
||||
MASK_ZERO,
|
||||
|
||||
SNDFLAG_LOOP=1,
|
||||
SNDFLAG_POS=2,
|
||||
SNDFLAG_ALL=3,
|
||||
|
||||
MAX_NAME_LENGTH=32
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SRVFLAG_PASSWORD = 0x1,
|
||||
};
|
||||
|
||||
/*
|
||||
Structure: SNAP_ITEM
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
int id;
|
||||
int datasize;
|
||||
} SNAP_ITEM;
|
||||
|
||||
/*
|
||||
Structure: CLIENT_INFO
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
int latency;
|
||||
} CLIENT_INFO;
|
||||
|
||||
typedef struct PERFORMACE_INFO_t
|
||||
{
|
||||
const char *name;
|
||||
struct PERFORMACE_INFO_t *parent;
|
||||
struct PERFORMACE_INFO_t *first_child;
|
||||
struct PERFORMACE_INFO_t *next_child;
|
||||
int tick;
|
||||
int64 start;
|
||||
int64 total;
|
||||
int64 biggest;
|
||||
int64 last_delta;
|
||||
} PERFORMACE_INFO;
|
||||
|
||||
void perf_init();
|
||||
void perf_next();
|
||||
void perf_start(PERFORMACE_INFO *info);
|
||||
void perf_end();
|
||||
void perf_dump(PERFORMACE_INFO *top);
|
||||
|
||||
int gfx_init();
|
||||
void gfx_shutdown();
|
||||
void gfx_swap();
|
||||
int gfx_window_active();
|
||||
int gfx_window_open();
|
||||
|
||||
void gfx_set_vsync(int val);
|
||||
|
||||
int snd_init();
|
||||
int snd_shutdown();
|
||||
int snd_update();
|
||||
|
||||
int map_load(const char *mapname);
|
||||
void map_unload();
|
||||
|
||||
void map_set(void *m);
|
||||
|
||||
/*
|
||||
#include "e_if_client.h"
|
||||
#include "e_if_server.h"
|
||||
#include "e_if_snd.h"
|
||||
#include "e_if_gfx.h"
|
||||
#include "e_if_inp.h"
|
||||
#include "e_if_msg.h"
|
||||
#include "e_if_mod.h"*/
|
||||
|
||||
/*
|
||||
Section: Map
|
||||
*/
|
||||
|
||||
/*
|
||||
Function: map_is_loaded
|
||||
Checks if a map is loaded.
|
||||
|
||||
Returns:
|
||||
Returns 1 if the button is pressed, otherwise 0.
|
||||
*/
|
||||
int map_is_loaded();
|
||||
|
||||
/*
|
||||
Function: map_num_items
|
||||
Checks the number of items in the loaded map.
|
||||
|
||||
Returns:
|
||||
Returns the number of items. 0 if no map is loaded.
|
||||
*/
|
||||
int map_num_items();
|
||||
|
||||
/*
|
||||
Function: map_find_item
|
||||
Searches the map for an item.
|
||||
|
||||
Arguments:
|
||||
type - Item type.
|
||||
id - Item ID.
|
||||
|
||||
Returns:
|
||||
Returns a pointer to the item if it exists, otherwise it returns NULL.
|
||||
*/
|
||||
void *map_find_item(int type, int id);
|
||||
|
||||
/*
|
||||
Function: map_get_item
|
||||
Gets an item from the loaded map from index.
|
||||
|
||||
Arguments:
|
||||
index - Item index.
|
||||
type - Pointer that recives the item type (can be NULL).
|
||||
id - Pointer that recives the item id (can be NULL).
|
||||
|
||||
Returns:
|
||||
Returns a pointer to the item if it exists, otherwise it returns NULL.
|
||||
*/
|
||||
void *map_get_item(int index, int *type, int *id);
|
||||
|
||||
/*
|
||||
Function: map_get_type
|
||||
Gets the index range of an item type.
|
||||
|
||||
Arguments:
|
||||
type - Item type to search for.
|
||||
start - Pointer that recives the starting index.
|
||||
num - Pointer that recives the number of items.
|
||||
|
||||
Returns:
|
||||
If the item type is not in the map, start and num will be set to 0.
|
||||
*/
|
||||
void map_get_type(int type, int *start, int *num);
|
||||
|
||||
/*
|
||||
Function: map_get_data
|
||||
Fetches a pointer to a raw data chunk in the map.
|
||||
|
||||
Arguments:
|
||||
index - Index to the data to fetch.
|
||||
|
||||
Returns:
|
||||
A pointer to the raw data, otherwise 0.
|
||||
*/
|
||||
void *map_get_data(int index);
|
||||
|
||||
/*
|
||||
Function: map_get_data_swapped
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void *map_get_data_swapped(int index);
|
||||
|
||||
/*
|
||||
Section: Network (Server)
|
||||
*/
|
||||
/*
|
||||
Function: snap_new_item
|
||||
Creates a new item that should be sent.
|
||||
|
||||
Arguments:
|
||||
type - Type of the item.
|
||||
id - ID of the item.
|
||||
size - Size of the item.
|
||||
|
||||
Returns:
|
||||
A pointer to the item data, otherwise 0.
|
||||
|
||||
Remarks:
|
||||
The item data should only consist pf 4 byte integers as
|
||||
they are subject to byte swapping. This means that the size
|
||||
argument should be dividable by 4.
|
||||
*/
|
||||
void *snap_new_item(int type, int id, int size);
|
||||
|
||||
/*
|
||||
Section:Section: Network (Client)
|
||||
*/
|
||||
/*
|
||||
Function: snap_num_items
|
||||
Check the number of items in a snapshot.
|
||||
|
||||
Arguments:
|
||||
snapid - Snapshot ID to the data to fetch.
|
||||
* SNAP_PREV for previous snapshot.
|
||||
* SNAP_CUR for current snapshot.
|
||||
|
||||
Returns:
|
||||
The number of items in the snapshot.
|
||||
*/
|
||||
int snap_num_items(int snapid);
|
||||
|
||||
/*
|
||||
Function: snap_get_item
|
||||
Gets an item from a snapshot.
|
||||
|
||||
Arguments:
|
||||
snapid - Snapshot ID to the data to fetch.
|
||||
* SNAP_PREV for previous snapshot.
|
||||
* SNAP_CUR for current snapshot.
|
||||
index - Index of the item.
|
||||
item - Pointer that recives the item info.
|
||||
|
||||
Returns:
|
||||
Returns a pointer to the item if it exists, otherwise NULL.
|
||||
*/
|
||||
void *snap_get_item(int snapid, int index, SNAP_ITEM *item);
|
||||
|
||||
/*
|
||||
Function: snap_find_item
|
||||
Searches a snapshot for an item.
|
||||
|
||||
Arguments:
|
||||
snapid - Snapshot ID to the data to fetch.
|
||||
* SNAP_PREV for previous snapshot.
|
||||
* SNAP_CUR for current snapshot.
|
||||
type - Type of the item.
|
||||
id - ID of the item.
|
||||
|
||||
Returns:
|
||||
Returns a pointer to the item if it exists, otherwise NULL.
|
||||
*/
|
||||
void *snap_find_item(int snapid, int type, int id);
|
||||
|
||||
/*
|
||||
Function: snap_invalidate_item
|
||||
Marks an item as invalid byt setting type and id to 0xffffffff.
|
||||
|
||||
Arguments:
|
||||
snapid - Snapshot ID to the data to fetch.
|
||||
* SNAP_PREV for previous snapshot.
|
||||
* SNAP_CUR for current snapshot.
|
||||
index - Index of the item.
|
||||
*/
|
||||
void snap_invalidate_item(int snapid, int index);
|
||||
|
||||
/*
|
||||
Function: snap_input
|
||||
Sets the input data to send to the server.
|
||||
|
||||
Arguments:
|
||||
data - Pointer to the data.
|
||||
size - Size of the data.
|
||||
|
||||
Remarks:
|
||||
The data should only consist of 4 bytes integer as they are
|
||||
subject to byte swapping.
|
||||
*/
|
||||
void snap_input(void *data, int size);
|
||||
|
||||
/*
|
||||
Function: snap_set_staticsize
|
||||
Tells the engine how big a specific item always will be. This
|
||||
helps the engine to compress snapshots better.
|
||||
|
||||
Arguments:
|
||||
type - Item type
|
||||
size - Size of the data.
|
||||
|
||||
Remarks:
|
||||
Size must be in a multiple of 4.
|
||||
*/
|
||||
void snap_set_staticsize(int type, int size);
|
||||
|
||||
/* message packing */
|
||||
enum
|
||||
{
|
||||
MSGFLAG_VITAL=1,
|
||||
MSGFLAG_FLUSH=2,
|
||||
MSGFLAG_NORECORD=4,
|
||||
MSGFLAG_RECORD=8,
|
||||
MSGFLAG_NOSEND=16
|
||||
};
|
||||
|
||||
/* message sending */
|
||||
/*
|
||||
Function: server_send_msg
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int server_send_msg(int client_id); /* client_id == -1 == broadcast */
|
||||
|
||||
/*
|
||||
Function: client_send_msg
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int client_send_msg();
|
||||
/* undocumented graphics stuff */
|
||||
|
||||
/* server snap id */
|
||||
/*
|
||||
Function: snap_new_id
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int snap_new_id();
|
||||
|
||||
/*
|
||||
Function: snap_free_id
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void snap_free_id(int id);
|
||||
|
||||
/* other */
|
||||
/*
|
||||
Function: map_unload_data
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void map_unload_data(int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,149 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_SERVER_H
|
||||
#define ENGINE_IF_SERVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
Section: Server Interface
|
||||
*/
|
||||
|
||||
/* server */
|
||||
/*
|
||||
Function: server_getclientinfo
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int server_getclientinfo(int client_id, CLIENT_INFO *info);
|
||||
|
||||
/*
|
||||
Function: server_clientname
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
const char *server_clientname(int client_id);
|
||||
|
||||
/* grabs the latest input for the client. not withholding anything */
|
||||
|
||||
/*
|
||||
Function: server_latestinput
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int *server_latestinput(int client_id, int *size);
|
||||
|
||||
/*
|
||||
Function: server_setclientname
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void server_setclientname(int client_id, const char *name);
|
||||
|
||||
/*
|
||||
Function: server_setclientscore
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void server_setclientscore(int client_id, int score);
|
||||
|
||||
/*
|
||||
Function: server_setbrowseinfo
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void server_setbrowseinfo(const char *game_type, int progression);
|
||||
|
||||
/*
|
||||
Function: server_kick
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
void server_kick(int client_id, const char *reason);
|
||||
|
||||
/*
|
||||
Function: server_tick
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int server_tick();
|
||||
|
||||
/*
|
||||
Function: server_tickspeed
|
||||
TODO
|
||||
|
||||
Arguments:
|
||||
arg1 - desc
|
||||
|
||||
Returns:
|
||||
|
||||
See Also:
|
||||
<other_func>
|
||||
*/
|
||||
int server_tickspeed();
|
||||
|
||||
int server_ban_add(NETADDR addr, int seconds);
|
||||
int server_ban_remove(NETADDR addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_IF_SND_H
|
||||
#define ENGINE_IF_SND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
Section: Sound
|
||||
*/
|
||||
|
||||
/*
|
||||
Function: snd_set_channel
|
||||
Sets the parameters for a sound channel.
|
||||
|
||||
Arguments:
|
||||
cid - Channel ID
|
||||
vol - Volume for the channel. 0.0 to 1.0.
|
||||
pan - Panning for the channel. -1.0 is all left. 0.0 is equal distribution. 1.0 is all right.
|
||||
*/
|
||||
void snd_set_channel(int cid, float vol, float pan);
|
||||
|
||||
/*
|
||||
Function: snd_load_wv
|
||||
Loads a wavpack compressed sound.
|
||||
|
||||
Arguments:
|
||||
filename - Filename of the file to load
|
||||
|
||||
Returns:
|
||||
The id of the loaded sound. -1 on failure.
|
||||
*/
|
||||
int snd_load_wv(const char *filename);
|
||||
|
||||
/*
|
||||
Function: snd_play_at
|
||||
Plays a sound at a specified postition.
|
||||
|
||||
Arguments:
|
||||
cid - Channel id of the channel to use.
|
||||
sid - Sound id of the sound to play.
|
||||
flags - TODO
|
||||
x - TODO
|
||||
y - TODO
|
||||
|
||||
Returns:
|
||||
An id to the voice. -1 on failure.
|
||||
|
||||
See Also:
|
||||
<snd_play, snd_stop>
|
||||
*/
|
||||
int snd_play_at(int cid, int sid, int flags, float x, float y);
|
||||
|
||||
/*
|
||||
Function: snd_play
|
||||
Plays a sound.
|
||||
|
||||
Arguments:
|
||||
Arguments:
|
||||
cid - Channel id of the channel to use.
|
||||
sid - Sound id of the sound to play.
|
||||
flags - TODO
|
||||
|
||||
Returns:
|
||||
An id to the voice. -1 on failure.
|
||||
|
||||
See Also:
|
||||
<snd_play_at, snd_stop>
|
||||
*/
|
||||
int snd_play(int cid, int sid, int flags);
|
||||
|
||||
/*
|
||||
Function: snd_stop
|
||||
Stops a currenly playing sound.
|
||||
|
||||
Arguments:
|
||||
id - The ID of the voice to stop.
|
||||
|
||||
See Also:
|
||||
<snd_play, snd_play_at>
|
||||
*/
|
||||
void snd_stop(int id);
|
||||
|
||||
/*
|
||||
Function: snd_set_listener_pos
|
||||
Sets the listener posititon.
|
||||
|
||||
Arguments:
|
||||
x - TODO
|
||||
y - TODO
|
||||
*/
|
||||
void snd_set_listener_pos(float x, float y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,76 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include "e_jobs.h"
|
||||
|
||||
void worker_thread(void *user)
|
||||
{
|
||||
JOBPOOL *pool = (JOBPOOL *)user;
|
||||
|
||||
while(1)
|
||||
{
|
||||
JOB *job = 0;
|
||||
|
||||
/* fetch job from queue */
|
||||
lock_wait(pool->lock);
|
||||
if(pool->first_job)
|
||||
{
|
||||
job = pool->first_job;
|
||||
pool->first_job = pool->first_job->next;
|
||||
if(pool->first_job)
|
||||
pool->first_job->prev = 0;
|
||||
else
|
||||
pool->last_job = 0;
|
||||
}
|
||||
lock_release(pool->lock);
|
||||
|
||||
/* do the job if we have one */
|
||||
if(job)
|
||||
{
|
||||
job->status = JOBSTATUS_RUNNING;
|
||||
job->result = job->func(job->func_data);
|
||||
job->status = JOBSTATUS_DONE;
|
||||
}
|
||||
else
|
||||
thread_sleep(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int jobs_initpool(JOBPOOL *pool, int num_threads)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* empty the pool */
|
||||
mem_zero(pool, sizeof(JOBPOOL));
|
||||
pool->lock = lock_create();
|
||||
|
||||
/* start threads */
|
||||
for(i = 0; i < num_threads; i++)
|
||||
thread_create(worker_thread, pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data)
|
||||
{
|
||||
mem_zero(job, sizeof(JOB));
|
||||
job->func = func;
|
||||
job->func_data = data;
|
||||
|
||||
lock_wait(pool->lock);
|
||||
|
||||
/* add job to queue */
|
||||
job->prev = pool->last_job;
|
||||
if(pool->last_job)
|
||||
pool->last_job->next = job;
|
||||
pool->last_job = job;
|
||||
if(!pool->first_job)
|
||||
pool->first_job = job;
|
||||
|
||||
lock_release(pool->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jobs_status(JOB *job)
|
||||
{
|
||||
return job->status;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef int (*JOBFUNC)(void *data);
|
||||
|
||||
typedef struct JOB
|
||||
{
|
||||
struct JOBPOOL *pool;
|
||||
struct JOB *prev;
|
||||
struct JOB *next;
|
||||
volatile int status;
|
||||
volatile int result;
|
||||
JOBFUNC func;
|
||||
void *func_data;
|
||||
} JOB;
|
||||
|
||||
typedef struct JOBPOOL
|
||||
{
|
||||
LOCK lock;
|
||||
JOB *first_job;
|
||||
JOB *last_job;
|
||||
} JOBPOOL;
|
||||
|
||||
enum
|
||||
{
|
||||
JOBSTATUS_PENDING=0,
|
||||
JOBSTATUS_RUNNING,
|
||||
JOBSTATUS_DONE
|
||||
/*JOBSTATUS_ABORTING,*/
|
||||
/*JOBSTATUS_ABORTED,*/
|
||||
};
|
||||
|
||||
int jobs_initpool(JOBPOOL *pool, int num_threads);
|
||||
int jobs_add(JOBPOOL *pool, JOB *job, JOBFUNC func, void *data);
|
||||
int jobs_status(JOB *job);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,525 +0,0 @@
|
||||
/* AUTO GENERATED! DO NOT EDIT MANUALLY! */
|
||||
|
||||
#include <string.h>
|
||||
#include "e_if_inp.h"
|
||||
|
||||
static const char key_strings[512][16] =
|
||||
{
|
||||
|
||||
"first",
|
||||
"&1",
|
||||
"&2",
|
||||
"&3",
|
||||
"&4",
|
||||
"&5",
|
||||
"&6",
|
||||
"&7",
|
||||
"backspace",
|
||||
"tab",
|
||||
"&10",
|
||||
"&11",
|
||||
"clear",
|
||||
"return",
|
||||
"&14",
|
||||
"&15",
|
||||
"&16",
|
||||
"&17",
|
||||
"&18",
|
||||
"pause",
|
||||
"&20",
|
||||
"&21",
|
||||
"&22",
|
||||
"&23",
|
||||
"&24",
|
||||
"&25",
|
||||
"&26",
|
||||
"escape",
|
||||
"&28",
|
||||
"&29",
|
||||
"&30",
|
||||
"&31",
|
||||
"space",
|
||||
"exclaim",
|
||||
"quotedbl",
|
||||
"hash",
|
||||
"dollar",
|
||||
"&37",
|
||||
"ampersand",
|
||||
"quote",
|
||||
"leftparen",
|
||||
"rightparen",
|
||||
"asterisk",
|
||||
"plus",
|
||||
"comma",
|
||||
"minus",
|
||||
"period",
|
||||
"slash",
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"colon",
|
||||
"semicolon",
|
||||
"less",
|
||||
"equals",
|
||||
"greater",
|
||||
"question",
|
||||
"at",
|
||||
"&65",
|
||||
"&66",
|
||||
"&67",
|
||||
"&68",
|
||||
"&69",
|
||||
"&70",
|
||||
"&71",
|
||||
"&72",
|
||||
"&73",
|
||||
"&74",
|
||||
"&75",
|
||||
"&76",
|
||||
"&77",
|
||||
"&78",
|
||||
"&79",
|
||||
"&80",
|
||||
"&81",
|
||||
"&82",
|
||||
"&83",
|
||||
"&84",
|
||||
"&85",
|
||||
"&86",
|
||||
"&87",
|
||||
"&88",
|
||||
"&89",
|
||||
"&90",
|
||||
"leftbracket",
|
||||
"backslash",
|
||||
"rightbracket",
|
||||
"caret",
|
||||
"underscore",
|
||||
"backquote",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"&123",
|
||||
"&124",
|
||||
"&125",
|
||||
"&126",
|
||||
"delete",
|
||||
"&128",
|
||||
"&129",
|
||||
"&130",
|
||||
"&131",
|
||||
"&132",
|
||||
"&133",
|
||||
"&134",
|
||||
"&135",
|
||||
"&136",
|
||||
"&137",
|
||||
"&138",
|
||||
"&139",
|
||||
"&140",
|
||||
"&141",
|
||||
"&142",
|
||||
"&143",
|
||||
"&144",
|
||||
"&145",
|
||||
"&146",
|
||||
"&147",
|
||||
"&148",
|
||||
"&149",
|
||||
"&150",
|
||||
"&151",
|
||||
"&152",
|
||||
"&153",
|
||||
"&154",
|
||||
"&155",
|
||||
"&156",
|
||||
"&157",
|
||||
"&158",
|
||||
"&159",
|
||||
"world_0",
|
||||
"world_1",
|
||||
"world_2",
|
||||
"world_3",
|
||||
"world_4",
|
||||
"world_5",
|
||||
"world_6",
|
||||
"world_7",
|
||||
"world_8",
|
||||
"world_9",
|
||||
"world_10",
|
||||
"world_11",
|
||||
"world_12",
|
||||
"world_13",
|
||||
"world_14",
|
||||
"world_15",
|
||||
"world_16",
|
||||
"world_17",
|
||||
"world_18",
|
||||
"world_19",
|
||||
"world_20",
|
||||
"world_21",
|
||||
"world_22",
|
||||
"world_23",
|
||||
"world_24",
|
||||
"world_25",
|
||||
"world_26",
|
||||
"world_27",
|
||||
"world_28",
|
||||
"world_29",
|
||||
"world_30",
|
||||
"world_31",
|
||||
"world_32",
|
||||
"world_33",
|
||||
"world_34",
|
||||
"world_35",
|
||||
"world_36",
|
||||
"world_37",
|
||||
"world_38",
|
||||
"world_39",
|
||||
"world_40",
|
||||
"world_41",
|
||||
"world_42",
|
||||
"world_43",
|
||||
"world_44",
|
||||
"world_45",
|
||||
"world_46",
|
||||
"world_47",
|
||||
"world_48",
|
||||
"world_49",
|
||||
"world_50",
|
||||
"world_51",
|
||||
"world_52",
|
||||
"world_53",
|
||||
"world_54",
|
||||
"world_55",
|
||||
"world_56",
|
||||
"world_57",
|
||||
"world_58",
|
||||
"world_59",
|
||||
"world_60",
|
||||
"world_61",
|
||||
"world_62",
|
||||
"world_63",
|
||||
"world_64",
|
||||
"world_65",
|
||||
"world_66",
|
||||
"world_67",
|
||||
"world_68",
|
||||
"world_69",
|
||||
"world_70",
|
||||
"world_71",
|
||||
"world_72",
|
||||
"world_73",
|
||||
"world_74",
|
||||
"world_75",
|
||||
"world_76",
|
||||
"world_77",
|
||||
"world_78",
|
||||
"world_79",
|
||||
"world_80",
|
||||
"world_81",
|
||||
"world_82",
|
||||
"world_83",
|
||||
"world_84",
|
||||
"world_85",
|
||||
"world_86",
|
||||
"world_87",
|
||||
"world_88",
|
||||
"world_89",
|
||||
"world_90",
|
||||
"world_91",
|
||||
"world_92",
|
||||
"world_93",
|
||||
"world_94",
|
||||
"world_95",
|
||||
"kp0",
|
||||
"kp1",
|
||||
"kp2",
|
||||
"kp3",
|
||||
"kp4",
|
||||
"kp5",
|
||||
"kp6",
|
||||
"kp7",
|
||||
"kp8",
|
||||
"kp9",
|
||||
"kp_period",
|
||||
"kp_divide",
|
||||
"kp_multiply",
|
||||
"kp_minus",
|
||||
"kp_plus",
|
||||
"kp_enter",
|
||||
"kp_equals",
|
||||
"up",
|
||||
"down",
|
||||
"right",
|
||||
"left",
|
||||
"insert",
|
||||
"home",
|
||||
"end",
|
||||
"pageup",
|
||||
"pagedown",
|
||||
"f1",
|
||||
"f2",
|
||||
"f3",
|
||||
"f4",
|
||||
"f5",
|
||||
"f6",
|
||||
"f7",
|
||||
"f8",
|
||||
"f9",
|
||||
"f10",
|
||||
"f11",
|
||||
"f12",
|
||||
"f13",
|
||||
"f14",
|
||||
"f15",
|
||||
"&297",
|
||||
"&298",
|
||||
"&299",
|
||||
"numlock",
|
||||
"capslock",
|
||||
"scrollock",
|
||||
"rshift",
|
||||
"lshift",
|
||||
"rctrl",
|
||||
"lctrl",
|
||||
"ralt",
|
||||
"lalt",
|
||||
"rmeta",
|
||||
"lmeta",
|
||||
"lsuper",
|
||||
"rsuper",
|
||||
"mode",
|
||||
"compose",
|
||||
"help",
|
||||
"print",
|
||||
"sysreq",
|
||||
"break",
|
||||
"menu",
|
||||
"power",
|
||||
"euro",
|
||||
"undo",
|
||||
"mouse1",
|
||||
"mouse2",
|
||||
"mouse3",
|
||||
"mouse4",
|
||||
"mouse5",
|
||||
"mouse6",
|
||||
"mouse7",
|
||||
"mouse8",
|
||||
"mousewheelup",
|
||||
"mousewheeldown",
|
||||
"&333",
|
||||
"&334",
|
||||
"&335",
|
||||
"&336",
|
||||
"&337",
|
||||
"&338",
|
||||
"&339",
|
||||
"&340",
|
||||
"&341",
|
||||
"&342",
|
||||
"&343",
|
||||
"&344",
|
||||
"&345",
|
||||
"&346",
|
||||
"&347",
|
||||
"&348",
|
||||
"&349",
|
||||
"&350",
|
||||
"&351",
|
||||
"&352",
|
||||
"&353",
|
||||
"&354",
|
||||
"&355",
|
||||
"&356",
|
||||
"&357",
|
||||
"&358",
|
||||
"&359",
|
||||
"&360",
|
||||
"&361",
|
||||
"&362",
|
||||
"&363",
|
||||
"&364",
|
||||
"&365",
|
||||
"&366",
|
||||
"&367",
|
||||
"&368",
|
||||
"&369",
|
||||
"&370",
|
||||
"&371",
|
||||
"&372",
|
||||
"&373",
|
||||
"&374",
|
||||
"&375",
|
||||
"&376",
|
||||
"&377",
|
||||
"&378",
|
||||
"&379",
|
||||
"&380",
|
||||
"&381",
|
||||
"&382",
|
||||
"&383",
|
||||
"&384",
|
||||
"&385",
|
||||
"&386",
|
||||
"&387",
|
||||
"&388",
|
||||
"&389",
|
||||
"&390",
|
||||
"&391",
|
||||
"&392",
|
||||
"&393",
|
||||
"&394",
|
||||
"&395",
|
||||
"&396",
|
||||
"&397",
|
||||
"&398",
|
||||
"&399",
|
||||
"&400",
|
||||
"&401",
|
||||
"&402",
|
||||
"&403",
|
||||
"&404",
|
||||
"&405",
|
||||
"&406",
|
||||
"&407",
|
||||
"&408",
|
||||
"&409",
|
||||
"&410",
|
||||
"&411",
|
||||
"&412",
|
||||
"&413",
|
||||
"&414",
|
||||
"&415",
|
||||
"&416",
|
||||
"&417",
|
||||
"&418",
|
||||
"&419",
|
||||
"&420",
|
||||
"&421",
|
||||
"&422",
|
||||
"&423",
|
||||
"&424",
|
||||
"&425",
|
||||
"&426",
|
||||
"&427",
|
||||
"&428",
|
||||
"&429",
|
||||
"&430",
|
||||
"&431",
|
||||
"&432",
|
||||
"&433",
|
||||
"&434",
|
||||
"&435",
|
||||
"&436",
|
||||
"&437",
|
||||
"&438",
|
||||
"&439",
|
||||
"&440",
|
||||
"&441",
|
||||
"&442",
|
||||
"&443",
|
||||
"&444",
|
||||
"&445",
|
||||
"&446",
|
||||
"&447",
|
||||
"&448",
|
||||
"&449",
|
||||
"&450",
|
||||
"&451",
|
||||
"&452",
|
||||
"&453",
|
||||
"&454",
|
||||
"&455",
|
||||
"&456",
|
||||
"&457",
|
||||
"&458",
|
||||
"&459",
|
||||
"&460",
|
||||
"&461",
|
||||
"&462",
|
||||
"&463",
|
||||
"&464",
|
||||
"&465",
|
||||
"&466",
|
||||
"&467",
|
||||
"&468",
|
||||
"&469",
|
||||
"&470",
|
||||
"&471",
|
||||
"&472",
|
||||
"&473",
|
||||
"&474",
|
||||
"&475",
|
||||
"&476",
|
||||
"&477",
|
||||
"&478",
|
||||
"&479",
|
||||
"&480",
|
||||
"&481",
|
||||
"&482",
|
||||
"&483",
|
||||
"&484",
|
||||
"&485",
|
||||
"&486",
|
||||
"&487",
|
||||
"&488",
|
||||
"&489",
|
||||
"&490",
|
||||
"&491",
|
||||
"&492",
|
||||
"&493",
|
||||
"&494",
|
||||
"&495",
|
||||
"&496",
|
||||
"&497",
|
||||
"&498",
|
||||
"&499",
|
||||
"&500",
|
||||
"&501",
|
||||
"&502",
|
||||
"&503",
|
||||
"&504",
|
||||
"&505",
|
||||
"&506",
|
||||
"&507",
|
||||
"&508",
|
||||
"&509",
|
||||
"&510",
|
||||
"&511",
|
||||
};
|
||||
|
||||
const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; }
|
||||
int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; }
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
#ifndef ENGINE_KEYS_H
|
||||
#define ENGINE_KEYS_H
|
||||
/* AUTO GENERATED! DO NOT EDIT MANUALLY! */
|
||||
enum
|
||||
{
|
||||
KEY_UNKNOWN = 0,
|
||||
KEY_FIRST = 0,
|
||||
KEY_BACKSPACE = 8,
|
||||
KEY_TAB = 9,
|
||||
KEY_CLEAR = 12,
|
||||
KEY_RETURN = 13,
|
||||
KEY_PAUSE = 19,
|
||||
KEY_ESCAPE = 27,
|
||||
KEY_SPACE = 32,
|
||||
KEY_EXCLAIM = 33,
|
||||
KEY_QUOTEDBL = 34,
|
||||
KEY_HASH = 35,
|
||||
KEY_DOLLAR = 36,
|
||||
KEY_AMPERSAND = 38,
|
||||
KEY_QUOTE = 39,
|
||||
KEY_LEFTPAREN = 40,
|
||||
KEY_RIGHTPAREN = 41,
|
||||
KEY_ASTERISK = 42,
|
||||
KEY_PLUS = 43,
|
||||
KEY_COMMA = 44,
|
||||
KEY_MINUS = 45,
|
||||
KEY_PERIOD = 46,
|
||||
KEY_SLASH = 47,
|
||||
KEY_0 = 48,
|
||||
KEY_1 = 49,
|
||||
KEY_2 = 50,
|
||||
KEY_3 = 51,
|
||||
KEY_4 = 52,
|
||||
KEY_5 = 53,
|
||||
KEY_6 = 54,
|
||||
KEY_7 = 55,
|
||||
KEY_8 = 56,
|
||||
KEY_9 = 57,
|
||||
KEY_COLON = 58,
|
||||
KEY_SEMICOLON = 59,
|
||||
KEY_LESS = 60,
|
||||
KEY_EQUALS = 61,
|
||||
KEY_GREATER = 62,
|
||||
KEY_QUESTION = 63,
|
||||
KEY_AT = 64,
|
||||
KEY_LEFTBRACKET = 91,
|
||||
KEY_BACKSLASH = 92,
|
||||
KEY_RIGHTBRACKET = 93,
|
||||
KEY_CARET = 94,
|
||||
KEY_UNDERSCORE = 95,
|
||||
KEY_BACKQUOTE = 96,
|
||||
KEY_a = 97,
|
||||
KEY_b = 98,
|
||||
KEY_c = 99,
|
||||
KEY_d = 100,
|
||||
KEY_e = 101,
|
||||
KEY_f = 102,
|
||||
KEY_g = 103,
|
||||
KEY_h = 104,
|
||||
KEY_i = 105,
|
||||
KEY_j = 106,
|
||||
KEY_k = 107,
|
||||
KEY_l = 108,
|
||||
KEY_m = 109,
|
||||
KEY_n = 110,
|
||||
KEY_o = 111,
|
||||
KEY_p = 112,
|
||||
KEY_q = 113,
|
||||
KEY_r = 114,
|
||||
KEY_s = 115,
|
||||
KEY_t = 116,
|
||||
KEY_u = 117,
|
||||
KEY_v = 118,
|
||||
KEY_w = 119,
|
||||
KEY_x = 120,
|
||||
KEY_y = 121,
|
||||
KEY_z = 122,
|
||||
KEY_DELETE = 127,
|
||||
KEY_WORLD_0 = 160,
|
||||
KEY_WORLD_1 = 161,
|
||||
KEY_WORLD_2 = 162,
|
||||
KEY_WORLD_3 = 163,
|
||||
KEY_WORLD_4 = 164,
|
||||
KEY_WORLD_5 = 165,
|
||||
KEY_WORLD_6 = 166,
|
||||
KEY_WORLD_7 = 167,
|
||||
KEY_WORLD_8 = 168,
|
||||
KEY_WORLD_9 = 169,
|
||||
KEY_WORLD_10 = 170,
|
||||
KEY_WORLD_11 = 171,
|
||||
KEY_WORLD_12 = 172,
|
||||
KEY_WORLD_13 = 173,
|
||||
KEY_WORLD_14 = 174,
|
||||
KEY_WORLD_15 = 175,
|
||||
KEY_WORLD_16 = 176,
|
||||
KEY_WORLD_17 = 177,
|
||||
KEY_WORLD_18 = 178,
|
||||
KEY_WORLD_19 = 179,
|
||||
KEY_WORLD_20 = 180,
|
||||
KEY_WORLD_21 = 181,
|
||||
KEY_WORLD_22 = 182,
|
||||
KEY_WORLD_23 = 183,
|
||||
KEY_WORLD_24 = 184,
|
||||
KEY_WORLD_25 = 185,
|
||||
KEY_WORLD_26 = 186,
|
||||
KEY_WORLD_27 = 187,
|
||||
KEY_WORLD_28 = 188,
|
||||
KEY_WORLD_29 = 189,
|
||||
KEY_WORLD_30 = 190,
|
||||
KEY_WORLD_31 = 191,
|
||||
KEY_WORLD_32 = 192,
|
||||
KEY_WORLD_33 = 193,
|
||||
KEY_WORLD_34 = 194,
|
||||
KEY_WORLD_35 = 195,
|
||||
KEY_WORLD_36 = 196,
|
||||
KEY_WORLD_37 = 197,
|
||||
KEY_WORLD_38 = 198,
|
||||
KEY_WORLD_39 = 199,
|
||||
KEY_WORLD_40 = 200,
|
||||
KEY_WORLD_41 = 201,
|
||||
KEY_WORLD_42 = 202,
|
||||
KEY_WORLD_43 = 203,
|
||||
KEY_WORLD_44 = 204,
|
||||
KEY_WORLD_45 = 205,
|
||||
KEY_WORLD_46 = 206,
|
||||
KEY_WORLD_47 = 207,
|
||||
KEY_WORLD_48 = 208,
|
||||
KEY_WORLD_49 = 209,
|
||||
KEY_WORLD_50 = 210,
|
||||
KEY_WORLD_51 = 211,
|
||||
KEY_WORLD_52 = 212,
|
||||
KEY_WORLD_53 = 213,
|
||||
KEY_WORLD_54 = 214,
|
||||
KEY_WORLD_55 = 215,
|
||||
KEY_WORLD_56 = 216,
|
||||
KEY_WORLD_57 = 217,
|
||||
KEY_WORLD_58 = 218,
|
||||
KEY_WORLD_59 = 219,
|
||||
KEY_WORLD_60 = 220,
|
||||
KEY_WORLD_61 = 221,
|
||||
KEY_WORLD_62 = 222,
|
||||
KEY_WORLD_63 = 223,
|
||||
KEY_WORLD_64 = 224,
|
||||
KEY_WORLD_65 = 225,
|
||||
KEY_WORLD_66 = 226,
|
||||
KEY_WORLD_67 = 227,
|
||||
KEY_WORLD_68 = 228,
|
||||
KEY_WORLD_69 = 229,
|
||||
KEY_WORLD_70 = 230,
|
||||
KEY_WORLD_71 = 231,
|
||||
KEY_WORLD_72 = 232,
|
||||
KEY_WORLD_73 = 233,
|
||||
KEY_WORLD_74 = 234,
|
||||
KEY_WORLD_75 = 235,
|
||||
KEY_WORLD_76 = 236,
|
||||
KEY_WORLD_77 = 237,
|
||||
KEY_WORLD_78 = 238,
|
||||
KEY_WORLD_79 = 239,
|
||||
KEY_WORLD_80 = 240,
|
||||
KEY_WORLD_81 = 241,
|
||||
KEY_WORLD_82 = 242,
|
||||
KEY_WORLD_83 = 243,
|
||||
KEY_WORLD_84 = 244,
|
||||
KEY_WORLD_85 = 245,
|
||||
KEY_WORLD_86 = 246,
|
||||
KEY_WORLD_87 = 247,
|
||||
KEY_WORLD_88 = 248,
|
||||
KEY_WORLD_89 = 249,
|
||||
KEY_WORLD_90 = 250,
|
||||
KEY_WORLD_91 = 251,
|
||||
KEY_WORLD_92 = 252,
|
||||
KEY_WORLD_93 = 253,
|
||||
KEY_WORLD_94 = 254,
|
||||
KEY_WORLD_95 = 255,
|
||||
KEY_KP0 = 256,
|
||||
KEY_KP1 = 257,
|
||||
KEY_KP2 = 258,
|
||||
KEY_KP3 = 259,
|
||||
KEY_KP4 = 260,
|
||||
KEY_KP5 = 261,
|
||||
KEY_KP6 = 262,
|
||||
KEY_KP7 = 263,
|
||||
KEY_KP8 = 264,
|
||||
KEY_KP9 = 265,
|
||||
KEY_KP_PERIOD = 266,
|
||||
KEY_KP_DIVIDE = 267,
|
||||
KEY_KP_MULTIPLY = 268,
|
||||
KEY_KP_MINUS = 269,
|
||||
KEY_KP_PLUS = 270,
|
||||
KEY_KP_ENTER = 271,
|
||||
KEY_KP_EQUALS = 272,
|
||||
KEY_UP = 273,
|
||||
KEY_DOWN = 274,
|
||||
KEY_RIGHT = 275,
|
||||
KEY_LEFT = 276,
|
||||
KEY_INSERT = 277,
|
||||
KEY_HOME = 278,
|
||||
KEY_END = 279,
|
||||
KEY_PAGEUP = 280,
|
||||
KEY_PAGEDOWN = 281,
|
||||
KEY_F1 = 282,
|
||||
KEY_F2 = 283,
|
||||
KEY_F3 = 284,
|
||||
KEY_F4 = 285,
|
||||
KEY_F5 = 286,
|
||||
KEY_F6 = 287,
|
||||
KEY_F7 = 288,
|
||||
KEY_F8 = 289,
|
||||
KEY_F9 = 290,
|
||||
KEY_F10 = 291,
|
||||
KEY_F11 = 292,
|
||||
KEY_F12 = 293,
|
||||
KEY_F13 = 294,
|
||||
KEY_F14 = 295,
|
||||
KEY_F15 = 296,
|
||||
KEY_NUMLOCK = 300,
|
||||
KEY_CAPSLOCK = 301,
|
||||
KEY_SCROLLOCK = 302,
|
||||
KEY_RSHIFT = 303,
|
||||
KEY_LSHIFT = 304,
|
||||
KEY_RCTRL = 305,
|
||||
KEY_LCTRL = 306,
|
||||
KEY_RALT = 307,
|
||||
KEY_LALT = 308,
|
||||
KEY_RMETA = 309,
|
||||
KEY_LMETA = 310,
|
||||
KEY_LSUPER = 311,
|
||||
KEY_RSUPER = 312,
|
||||
KEY_MODE = 313,
|
||||
KEY_COMPOSE = 314,
|
||||
KEY_HELP = 315,
|
||||
KEY_PRINT = 316,
|
||||
KEY_SYSREQ = 317,
|
||||
KEY_BREAK = 318,
|
||||
KEY_MENU = 319,
|
||||
KEY_POWER = 320,
|
||||
KEY_EURO = 321,
|
||||
KEY_UNDO = 322,
|
||||
KEY_MOUSE_1 = 323,
|
||||
KEY_MOUSE_2 = 324,
|
||||
KEY_MOUSE_3 = 325,
|
||||
KEY_MOUSE_4 = 326,
|
||||
KEY_MOUSE_5 = 327,
|
||||
KEY_MOUSE_6 = 328,
|
||||
KEY_MOUSE_7 = 329,
|
||||
KEY_MOUSE_8 = 330,
|
||||
KEY_MOUSE_WHEEL_UP = 331,
|
||||
KEY_MOUSE_WHEEL_DOWN = 332,
|
||||
KEY_LAST,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
#include "e_linereader.h"
|
||||
|
||||
void linereader_init(LINEREADER *lr, IOHANDLE io)
|
||||
{
|
||||
lr->buffer_max_size = 4*1024;
|
||||
lr->buffer_size = 0;
|
||||
lr->buffer_pos = 0;
|
||||
lr->io = io;
|
||||
}
|
||||
|
||||
char *linereader_get(LINEREADER *lr)
|
||||
{
|
||||
unsigned line_start = lr->buffer_pos;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if(lr->buffer_pos >= lr->buffer_size)
|
||||
{
|
||||
/* fetch more */
|
||||
|
||||
/* move the remaining part to the front */
|
||||
unsigned read;
|
||||
unsigned left = lr->buffer_size - line_start;
|
||||
|
||||
if(line_start > lr->buffer_size)
|
||||
left = 0;
|
||||
if(left)
|
||||
mem_move(lr->buffer, &lr->buffer[line_start], left);
|
||||
lr->buffer_pos = left;
|
||||
|
||||
/* fill the buffer */
|
||||
read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos);
|
||||
lr->buffer_size = left + read;
|
||||
line_start = 0;
|
||||
|
||||
if(!read)
|
||||
{
|
||||
if(left)
|
||||
{
|
||||
lr->buffer[left] = 0; /* return the last line */
|
||||
lr->buffer_pos = left;
|
||||
lr->buffer_size = left;
|
||||
return lr->buffer;
|
||||
}
|
||||
else
|
||||
return 0x0; /* we are done! */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r')
|
||||
{
|
||||
/* line found */
|
||||
lr->buffer[lr->buffer_pos] = 0;
|
||||
lr->buffer_pos++;
|
||||
return &lr->buffer[line_start];
|
||||
}
|
||||
else
|
||||
lr->buffer_pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <base/system.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* buffered stream for reading lines, should perhaps be something smaller */
|
||||
typedef struct
|
||||
{
|
||||
char buffer[4*1024];
|
||||
unsigned buffer_pos;
|
||||
unsigned buffer_size;
|
||||
unsigned buffer_max_size;
|
||||
IOHANDLE io;
|
||||
} LINEREADER;
|
||||
|
||||
void linereader_init(LINEREADER *lr, IOHANDLE io);
|
||||
char *linereader_get(LINEREADER *lr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,67 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include "e_datafile.h"
|
||||
#include "e_if_other.h"
|
||||
|
||||
static DATAFILE *map = 0;
|
||||
|
||||
void *map_get_data(int index)
|
||||
{
|
||||
return datafile_get_data(map, index);
|
||||
}
|
||||
|
||||
void *map_get_data_swapped(int index)
|
||||
{
|
||||
return datafile_get_data_swapped(map, index);
|
||||
}
|
||||
|
||||
void map_unload_data(int index)
|
||||
{
|
||||
datafile_unload_data(map, index);
|
||||
}
|
||||
|
||||
void *map_get_item(int index, int *type, int *id)
|
||||
{
|
||||
return datafile_get_item(map, index, type, id);
|
||||
}
|
||||
|
||||
void map_get_type(int type, int *start, int *num)
|
||||
{
|
||||
datafile_get_type(map, type, start, num);
|
||||
}
|
||||
|
||||
void *map_find_item(int type, int id)
|
||||
{
|
||||
return datafile_find_item(map, type, id);
|
||||
}
|
||||
|
||||
int map_num_items()
|
||||
{
|
||||
return datafile_num_items(map);
|
||||
}
|
||||
|
||||
void map_unload()
|
||||
{
|
||||
datafile_unload(map);
|
||||
map = 0x0;
|
||||
}
|
||||
|
||||
int map_is_loaded()
|
||||
{
|
||||
return map != 0;
|
||||
}
|
||||
|
||||
int map_load(const char *mapname)
|
||||
{
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "maps/%s.map", mapname);
|
||||
map = datafile_load(buf);
|
||||
return map != 0;
|
||||
}
|
||||
|
||||
void map_set(void *m)
|
||||
{
|
||||
if(map)
|
||||
map_unload();
|
||||
map = (DATAFILE*)m;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include "e_memheap.h"
|
||||
|
||||
typedef struct CHUNK_t
|
||||
{
|
||||
char *memory;
|
||||
char *current;
|
||||
char *end;
|
||||
struct CHUNK_t *next;
|
||||
} CHUNK;
|
||||
|
||||
typedef struct HEAP_t
|
||||
{
|
||||
CHUNK *current;
|
||||
} HEAP;
|
||||
|
||||
/* how large each chunk should be */
|
||||
static const int chunksize = 1024*64;
|
||||
|
||||
/* allocates a new chunk to be used */
|
||||
static CHUNK *memheap_newchunk()
|
||||
{
|
||||
CHUNK *chunk;
|
||||
char *mem;
|
||||
|
||||
/* allocate memory */
|
||||
mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1);
|
||||
if(!mem)
|
||||
return 0x0;
|
||||
|
||||
/* the chunk structure is located in the begining of the chunk */
|
||||
/* init it and return the chunk */
|
||||
chunk = (CHUNK*)mem;
|
||||
chunk->memory = (char*)(chunk+1);
|
||||
chunk->current = chunk->memory;
|
||||
chunk->end = chunk->memory + chunksize;
|
||||
chunk->next = (CHUNK *)0x0;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/******************/
|
||||
static void *memheap_allocate_from_chunk(CHUNK *chunk, unsigned int size)
|
||||
{
|
||||
char *mem;
|
||||
|
||||
/* check if we need can fit the allocation */
|
||||
if(chunk->current + size > chunk->end)
|
||||
return (void*)0x0;
|
||||
|
||||
/* get memory and move the pointer forward */
|
||||
mem = chunk->current;
|
||||
chunk->current += size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/* creates a heap */
|
||||
HEAP *memheap_create()
|
||||
{
|
||||
CHUNK *chunk;
|
||||
HEAP *heap;
|
||||
|
||||
/* allocate a chunk and allocate the heap structure on that chunk */
|
||||
chunk = memheap_newchunk();
|
||||
heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP));
|
||||
heap->current = chunk;
|
||||
return heap;
|
||||
}
|
||||
|
||||
/* destroys the heap */
|
||||
void memheap_destroy(HEAP *heap)
|
||||
{
|
||||
CHUNK *chunk = heap->current;
|
||||
CHUNK *next;
|
||||
|
||||
while(chunk)
|
||||
{
|
||||
next = chunk->next;
|
||||
mem_free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* */
|
||||
void *memheap_allocate(HEAP *heap, unsigned int size)
|
||||
{
|
||||
char *mem;
|
||||
|
||||
/* try to allocate from current chunk */
|
||||
mem = (char *)memheap_allocate_from_chunk(heap->current, size);
|
||||
if(!mem)
|
||||
{
|
||||
/* allocate new chunk and add it to the heap */
|
||||
CHUNK *chunk = memheap_newchunk();
|
||||
chunk->next = heap->current;
|
||||
heap->current = chunk;
|
||||
|
||||
/* try to allocate again */
|
||||
mem = (char *)memheap_allocate_from_chunk(heap->current, size);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct HEAP_t HEAP;
|
||||
HEAP *memheap_create();
|
||||
void memheap_destroy(HEAP *heap);
|
||||
void *memheap_allocate(HEAP *heap, unsigned int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include "e_common_interface.h"
|
||||
#include "e_packer.h"
|
||||
|
||||
/* message packing */
|
||||
static PACKER msg_packer;
|
||||
static MSG_INFO pack_info;
|
||||
static int packer_failed = 0;
|
||||
|
||||
void msg_pack_int(int i) { packer_add_int(&msg_packer, i); }
|
||||
void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); }
|
||||
void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); }
|
||||
|
||||
void msg_pack_start_system(int msg, int flags)
|
||||
{
|
||||
packer_reset(&msg_packer);
|
||||
pack_info.msg = (msg<<1)|1;
|
||||
pack_info.flags = flags;
|
||||
packer_failed = 0;
|
||||
|
||||
msg_pack_int(pack_info.msg);
|
||||
}
|
||||
|
||||
void msg_pack_start(int msg, int flags)
|
||||
{
|
||||
packer_reset(&msg_packer);
|
||||
pack_info.msg = msg<<1;
|
||||
pack_info.flags = flags;
|
||||
packer_failed = 0;
|
||||
|
||||
msg_pack_int(pack_info.msg);
|
||||
}
|
||||
|
||||
void msg_pack_end()
|
||||
{
|
||||
if(msg_packer.error)
|
||||
{
|
||||
packer_failed = 1;
|
||||
pack_info.size = 0;
|
||||
pack_info.data = (unsigned char *)"";
|
||||
}
|
||||
else
|
||||
{
|
||||
pack_info.size = packer_size(&msg_packer);
|
||||
pack_info.data = packer_data(&msg_packer);
|
||||
}
|
||||
}
|
||||
|
||||
const MSG_INFO *msg_get_info()
|
||||
{
|
||||
if(packer_failed)
|
||||
return 0;
|
||||
return &pack_info;
|
||||
}
|
||||
|
||||
/* message unpacking */
|
||||
static UNPACKER msg_unpacker;
|
||||
int msg_unpack_start(const void *data, int data_size, int *system)
|
||||
{
|
||||
int msg;
|
||||
unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size);
|
||||
msg = msg_unpack_int();
|
||||
*system = msg&1;
|
||||
return msg>>1;
|
||||
}
|
||||
|
||||
int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); }
|
||||
const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); }
|
||||
const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); }
|
||||
int msg_unpack_error() { return msg_unpacker.error; }
|
||||
@@ -1,345 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
|
||||
#include <string.h> /* strlen */
|
||||
|
||||
#include "e_config.h"
|
||||
#include "e_engine.h"
|
||||
#include "e_network.h"
|
||||
#include "e_network_internal.h"
|
||||
#include "e_huffman.h"
|
||||
|
||||
void recvinfo_clear(NETRECVINFO *info)
|
||||
{
|
||||
info->valid = 0;
|
||||
}
|
||||
|
||||
void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid)
|
||||
{
|
||||
info->addr = *addr;
|
||||
info->conn = conn;
|
||||
info->client_id = cid;
|
||||
info->current_chunk = 0;
|
||||
info->valid = 1;
|
||||
}
|
||||
|
||||
|
||||
int seq_in_backroom(int seq, int ack)
|
||||
{
|
||||
int bottom = (ack-NET_MAX_SEQUENCE/2);
|
||||
if(bottom < 0)
|
||||
{
|
||||
if(seq <= ack)
|
||||
return 1;
|
||||
if(seq >= (bottom + NET_MAX_SEQUENCE))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(seq <= ack && seq >= bottom)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: rename this function */
|
||||
int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk)
|
||||
{
|
||||
NETCHUNKHEADER header;
|
||||
unsigned char *end = info->data.chunk_data + info->data.data_size;
|
||||
int i;
|
||||
|
||||
while(1)
|
||||
{
|
||||
unsigned char *data = info->data.chunk_data;
|
||||
|
||||
/* check for old data to unpack */
|
||||
if(!info->valid || info->current_chunk >= info->data.num_chunks)
|
||||
{
|
||||
recvinfo_clear(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: add checking here so we don't read too far */
|
||||
for(i = 0; i < info->current_chunk; i++)
|
||||
{
|
||||
data = unpack_chunk_header(data, &header);
|
||||
data += header.size;
|
||||
}
|
||||
|
||||
/* unpack the header */
|
||||
data = unpack_chunk_header(data, &header);
|
||||
info->current_chunk++;
|
||||
|
||||
if(data+header.size > end)
|
||||
{
|
||||
recvinfo_clear(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle sequence stuff */
|
||||
if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL))
|
||||
{
|
||||
if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE)
|
||||
{
|
||||
/* in sequence */
|
||||
info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* old packet that we already got */
|
||||
if(seq_in_backroom(header.sequence, info->conn->ack))
|
||||
continue;
|
||||
|
||||
/* out of sequence, request resend */
|
||||
if(config.debug)
|
||||
dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE);
|
||||
conn_want_resend(info->conn);
|
||||
continue; /* take the next chunk in the packet */
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in the info */
|
||||
chunk->client_id = info->client_id;
|
||||
chunk->address = info->addr;
|
||||
chunk->flags = 0;
|
||||
chunk->data_size = header.size;
|
||||
chunk->data = data;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static IOHANDLE datalog_sent = 0;
|
||||
static IOHANDLE datalog_recv = 0;
|
||||
static HUFFMAN_STATE huffmanstate;
|
||||
|
||||
#define COMPRESSION 1
|
||||
|
||||
/* packs the data tight and sends it */
|
||||
void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size)
|
||||
{
|
||||
unsigned char buffer[NET_MAX_PACKETSIZE];
|
||||
buffer[0] = 0xff;
|
||||
buffer[1] = 0xff;
|
||||
buffer[2] = 0xff;
|
||||
buffer[3] = 0xff;
|
||||
buffer[4] = 0xff;
|
||||
buffer[5] = 0xff;
|
||||
mem_copy(&buffer[6], data, data_size);
|
||||
net_udp_send(socket, addr, buffer, 6+data_size);
|
||||
}
|
||||
|
||||
int netcommon_compress(const void *data, int data_size, void *output, int output_size)
|
||||
{
|
||||
return huffman_compress(&huffmanstate, data, data_size, output, output_size);
|
||||
}
|
||||
|
||||
int netcommon_decompress(const void *data, int data_size, void *output, int output_size)
|
||||
{
|
||||
return huffman_decompress(&huffmanstate, data, data_size, output, output_size);
|
||||
}
|
||||
|
||||
void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet)
|
||||
{
|
||||
unsigned char buffer[NET_MAX_PACKETSIZE];
|
||||
int compressed_size = -1;
|
||||
int final_size = -1;
|
||||
|
||||
/* log the data */
|
||||
if(datalog_sent)
|
||||
{
|
||||
int type = 1;
|
||||
io_write(datalog_sent, &type, sizeof(type));
|
||||
io_write(datalog_sent, &packet->data_size, sizeof(packet->data_size));
|
||||
io_write(datalog_sent, &packet->chunk_data, packet->data_size);
|
||||
io_flush(datalog_sent);
|
||||
}
|
||||
|
||||
/* compress if its enabled */
|
||||
if(COMPRESSION)
|
||||
compressed_size = huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[3], NET_MAX_PACKETSIZE-4);
|
||||
|
||||
/* check if the compression was enabled, successful and good enough */
|
||||
if(compressed_size > 0 && compressed_size < packet->data_size)
|
||||
{
|
||||
final_size = compressed_size;
|
||||
packet->flags |= NET_PACKETFLAG_COMPRESSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use uncompressed data */
|
||||
final_size = packet->data_size;
|
||||
mem_copy(&buffer[3], packet->chunk_data, packet->data_size);
|
||||
packet->flags &= ~NET_PACKETFLAG_COMPRESSION;
|
||||
}
|
||||
|
||||
/* set header and send the packet if all things are good */
|
||||
if(final_size >= 0)
|
||||
{
|
||||
final_size += NET_PACKETHEADERSIZE;
|
||||
buffer[0] = ((packet->flags<<4)&0xf0)|((packet->ack>>8)&0xf);
|
||||
buffer[1] = packet->ack&0xff;
|
||||
buffer[2] = packet->num_chunks;
|
||||
net_udp_send(socket, addr, buffer, final_size);
|
||||
|
||||
/* log raw socket data */
|
||||
if(datalog_sent)
|
||||
{
|
||||
int type = 0;
|
||||
io_write(datalog_sent, &type, sizeof(type));
|
||||
io_write(datalog_sent, &final_size, sizeof(final_size));
|
||||
io_write(datalog_sent, buffer, final_size);
|
||||
io_flush(datalog_sent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: rename this function */
|
||||
int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet)
|
||||
{
|
||||
/* check the size */
|
||||
if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE)
|
||||
{
|
||||
dbg_msg("", "packet too small, %d", size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* log the data */
|
||||
if(datalog_recv)
|
||||
{
|
||||
int type = 0;
|
||||
io_write(datalog_recv, &type, sizeof(type));
|
||||
io_write(datalog_recv, &size, sizeof(size));
|
||||
io_write(datalog_recv, buffer, size);
|
||||
io_flush(datalog_recv);
|
||||
}
|
||||
|
||||
/* read the packet */
|
||||
packet->flags = buffer[0]>>4;
|
||||
packet->ack = ((buffer[0]&0xf)<<8) | buffer[1];
|
||||
packet->num_chunks = buffer[2];
|
||||
packet->data_size = size - NET_PACKETHEADERSIZE;
|
||||
|
||||
if(packet->flags&NET_PACKETFLAG_CONNLESS)
|
||||
{
|
||||
packet->flags = NET_PACKETFLAG_CONNLESS;
|
||||
packet->ack = 0;
|
||||
packet->num_chunks = 0;
|
||||
packet->data_size = size - 6;
|
||||
mem_copy(packet->chunk_data, &buffer[6], packet->data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(packet->flags&NET_PACKETFLAG_COMPRESSION)
|
||||
packet->data_size = huffman_decompress(&huffmanstate, &buffer[3], packet->data_size, packet->chunk_data, sizeof(packet->chunk_data));
|
||||
else
|
||||
mem_copy(packet->chunk_data, &buffer[3], packet->data_size);
|
||||
}
|
||||
|
||||
/* check for errors */
|
||||
if(packet->data_size < 0)
|
||||
{
|
||||
if(config.debug)
|
||||
dbg_msg("network", "error during packet decoding");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* log the data */
|
||||
if(datalog_recv)
|
||||
{
|
||||
int type = 1;
|
||||
io_write(datalog_recv, &type, sizeof(type));
|
||||
io_write(datalog_recv, &packet->data_size, sizeof(packet->data_size));
|
||||
io_write(datalog_recv, packet->chunk_data, packet->data_size);
|
||||
io_flush(datalog_recv);
|
||||
}
|
||||
|
||||
/* return success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* TODO: change the arguments of this function */
|
||||
unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence)
|
||||
{
|
||||
data[0] = ((flags&3)<<6)|((size>>4)&0x3f);
|
||||
data[1] = (size&0xf);
|
||||
if(flags&NET_CHUNKFLAG_VITAL)
|
||||
{
|
||||
data[1] |= (sequence>>2)&0xf0;
|
||||
data[2] = sequence&0xff;
|
||||
return data + 3;
|
||||
}
|
||||
return data + 2;
|
||||
}
|
||||
|
||||
unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header)
|
||||
{
|
||||
header->flags = (data[0]>>6)&3;
|
||||
header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf);
|
||||
header->sequence = -1;
|
||||
if(header->flags&NET_CHUNKFLAG_VITAL)
|
||||
{
|
||||
header->sequence = ((data[1]&0xf0)<<2) | data[2];
|
||||
return data + 3;
|
||||
}
|
||||
return data + 2;
|
||||
}
|
||||
|
||||
|
||||
void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size)
|
||||
{
|
||||
NETPACKETCONSTRUCT construct;
|
||||
construct.flags = NET_PACKETFLAG_CONTROL;
|
||||
construct.ack = ack;
|
||||
construct.num_chunks = 0;
|
||||
construct.data_size = 1+extra_size;
|
||||
construct.chunk_data[0] = controlmsg;
|
||||
mem_copy(&construct.chunk_data[1], extra, extra_size);
|
||||
|
||||
/* send the control message */
|
||||
send_packet(socket, addr, &construct);
|
||||
}
|
||||
|
||||
void netcommon_openlog(const char *sentlog, const char *recvlog)
|
||||
{
|
||||
if(sentlog)
|
||||
{
|
||||
datalog_sent = engine_openfile(sentlog, IOFLAG_WRITE);
|
||||
if(datalog_sent)
|
||||
dbg_msg("network", "logging sent packages to '%s'", sentlog);
|
||||
else
|
||||
dbg_msg("network", "failed to open for logging '%s'", sentlog);
|
||||
}
|
||||
|
||||
if(recvlog)
|
||||
{
|
||||
datalog_recv = engine_openfile(recvlog, IOFLAG_WRITE);
|
||||
if(recvlog)
|
||||
dbg_msg("network", "logging recv packages to '%s'", recvlog);
|
||||
else
|
||||
dbg_msg("network", "failed to open for logging '%s'", recvlog);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned freq_table[256+1] = {
|
||||
1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186,
|
||||
283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176,
|
||||
872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19,
|
||||
16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68,
|
||||
59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9,
|
||||
20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5,
|
||||
15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71,
|
||||
148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80,
|
||||
167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60,
|
||||
136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21,
|
||||
32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21,
|
||||
12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18,
|
||||
9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517};
|
||||
|
||||
void netcommon_init()
|
||||
{
|
||||
huffman_init(&huffmanstate, freq_table);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
#ifndef ENGINE_NETWORK_H
|
||||
#define ENGINE_NETWORK_H
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* -1 means that it's a stateless packet */
|
||||
/* 0 on the client means the server */
|
||||
int client_id;
|
||||
NETADDR address; /* only used when client_id == -1 */
|
||||
int flags;
|
||||
int data_size;
|
||||
const void *data;
|
||||
} NETCHUNK;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NETADDR addr;
|
||||
int expires;
|
||||
} NETBANINFO;
|
||||
|
||||
/*typedef struct
|
||||
{
|
||||
int send_bytes;
|
||||
int recv_bytes;
|
||||
int send_packets;
|
||||
int recv_packets;
|
||||
|
||||
int resend_packets;
|
||||
int resend_bytes;
|
||||
} NETSTATS;*/
|
||||
|
||||
typedef struct NETSERVER NETSERVER;
|
||||
typedef struct NETCLIENT NETCLIENT;
|
||||
|
||||
enum
|
||||
{
|
||||
NETFLAG_ALLOWSTATELESS=1,
|
||||
NETSENDFLAG_VITAL=1,
|
||||
NETSENDFLAG_CONNLESS=2,
|
||||
NETSENDFLAG_FLUSH=4,
|
||||
|
||||
NETSTATE_OFFLINE=0,
|
||||
NETSTATE_CONNECTING,
|
||||
NETSTATE_ONLINE,
|
||||
|
||||
NETBANTYPE_SOFT=1,
|
||||
NETBANTYPE_DROP=2
|
||||
};
|
||||
|
||||
typedef int (*NETFUNC_DELCLIENT)(int cid, void *user);
|
||||
typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user);
|
||||
|
||||
/* both */
|
||||
void netcommon_openlog(const char *sentlog, const char *recvlog);
|
||||
void netcommon_init();
|
||||
int netcommon_compress(const void *data, int data_size, void *output, int output_size);
|
||||
int netcommon_decompress(const void *data, int data_size, void *output, int output_size);
|
||||
|
||||
/* server side */
|
||||
NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags);
|
||||
int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user);
|
||||
int netserver_recv(NETSERVER *s, NETCHUNK *chunk);
|
||||
int netserver_send(NETSERVER *s, NETCHUNK *chunk);
|
||||
int netserver_close(NETSERVER *s);
|
||||
int netserver_update(NETSERVER *s);
|
||||
NETSOCKET netserver_socket(NETSERVER *s);
|
||||
int netserver_drop(NETSERVER *s, int client_id, const char *reason);
|
||||
int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr);
|
||||
int netserver_max_clients(NETSERVER *s);
|
||||
|
||||
int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds);
|
||||
int netserver_ban_remove(NETSERVER *s, NETADDR addr);
|
||||
int netserver_ban_num(NETSERVER *s); /* caution, slow */
|
||||
int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */
|
||||
|
||||
/*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/
|
||||
|
||||
/* client side */
|
||||
NETCLIENT *netclient_open(NETADDR bindaddr, int flags);
|
||||
int netclient_disconnect(NETCLIENT *c, const char *reason);
|
||||
int netclient_connect(NETCLIENT *c, NETADDR *addr);
|
||||
int netclient_recv(NETCLIENT *c, NETCHUNK *chunk);
|
||||
int netclient_send(NETCLIENT *c, NETCHUNK *chunk);
|
||||
int netclient_close(NETCLIENT *c);
|
||||
int netclient_update(NETCLIENT *c);
|
||||
int netclient_state(NETCLIENT *c);
|
||||
int netclient_flush(NETCLIENT *c);
|
||||
int netclient_gotproblems(NETCLIENT *c);
|
||||
/*void netclient_stats(NETCLIENT *c, NETSTATS *stats);*/
|
||||
int netclient_error_string_reset(NETCLIENT *c);
|
||||
const char *netclient_error_string(NETCLIENT *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
class net_server
|
||||
{
|
||||
NETSERVER *ptr;
|
||||
public:
|
||||
net_server() : ptr(0) {}
|
||||
~net_server() { close(); }
|
||||
|
||||
int open(NETADDR bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; }
|
||||
int close() { int r = netserver_close(ptr); ptr = 0; return r; }
|
||||
|
||||
int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
|
||||
{ return netserver_set_callbacks(ptr, new_client, del_client, user); }
|
||||
|
||||
int recv(NETCHUNK *chunk) { return netserver_recv(ptr, chunk); }
|
||||
int send(NETCHUNK *chunk) { return netserver_send(ptr, chunk); }
|
||||
int update() { return netserver_update(ptr); }
|
||||
|
||||
int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); }
|
||||
|
||||
int max_clients() { return netserver_max_clients(ptr); }
|
||||
/*void stats(NETSTATS *stats) { netserver_stats(ptr, stats); }*/
|
||||
};
|
||||
|
||||
|
||||
class net_client
|
||||
{
|
||||
NETCLIENT *ptr;
|
||||
public:
|
||||
net_client() : ptr(0) {}
|
||||
~net_client() { close(); }
|
||||
|
||||
int open(NETADDR bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; }
|
||||
int close() { int r = netclient_close(ptr); ptr = 0; return r; }
|
||||
|
||||
int connect(NETADDR *addr) { return netclient_connect(ptr, addr); }
|
||||
int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); }
|
||||
|
||||
int recv(NETCHUNK *chunk) { return netclient_recv(ptr, chunk); }
|
||||
int send(NETCHUNK *chunk) { return netclient_send(ptr, chunk); }
|
||||
int update() { return netclient_update(ptr); }
|
||||
|
||||
const char *error_string() { return netclient_error_string(ptr); }
|
||||
|
||||
int state() { return netclient_state(ptr); }
|
||||
/*void stats(NETSTATS *stats) { netclient_stats(ptr, stats); }*/
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,154 +0,0 @@
|
||||
#include <base/system.h>
|
||||
#include "e_network.h"
|
||||
#include "e_network_internal.h"
|
||||
|
||||
struct NETCLIENT
|
||||
{
|
||||
NETADDR server_addr;
|
||||
NETSOCKET socket;
|
||||
|
||||
NETRECVINFO recv;
|
||||
NETCONNECTION conn;
|
||||
};
|
||||
|
||||
NETCLIENT *netclient_open(NETADDR bindaddr, int flags)
|
||||
{
|
||||
NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
|
||||
mem_zero(client, sizeof(NETCLIENT));
|
||||
client->socket = net_udp_create(bindaddr);
|
||||
conn_init(&client->conn, client->socket);
|
||||
return client;
|
||||
}
|
||||
|
||||
int netclient_close(NETCLIENT *c)
|
||||
{
|
||||
/* TODO: implement me */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int netclient_disconnect(NETCLIENT *c, const char *reason)
|
||||
{
|
||||
dbg_msg("netclient", "disconnected. reason=\"%s\"", reason);
|
||||
conn_disconnect(&c->conn, reason);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_update(NETCLIENT *c)
|
||||
{
|
||||
conn_update(&c->conn);
|
||||
if(c->conn.state == NET_CONNSTATE_ERROR)
|
||||
netclient_disconnect(c, conn_error(&c->conn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_connect(NETCLIENT *c, NETADDR *addr)
|
||||
{
|
||||
conn_connect(&c->conn, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_error_string_reset(NETCLIENT *c)
|
||||
{
|
||||
mem_zero(c->conn.error_string, sizeof(c->conn.error_string));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_recv(NETCLIENT *c, NETCHUNK *chunk)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
NETADDR addr;
|
||||
int bytes;
|
||||
|
||||
/* check for a chunk */
|
||||
if(recvinfo_fetch_chunk(&c->recv, chunk))
|
||||
return 1;
|
||||
|
||||
/* TODO: empty the recvinfo */
|
||||
bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE);
|
||||
|
||||
/* no more packets for now */
|
||||
if(bytes <= 0)
|
||||
break;
|
||||
|
||||
if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0)
|
||||
{
|
||||
if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS)
|
||||
{
|
||||
chunk->flags = NETSENDFLAG_CONNLESS;
|
||||
chunk->client_id = -1;
|
||||
chunk->address = addr;
|
||||
chunk->data_size = c->recv.data.data_size;
|
||||
chunk->data = c->recv.data.chunk_data;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(conn_feed(&c->conn, &c->recv.data, &addr))
|
||||
recvinfo_start(&c->recv, &addr, &c->conn, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_send(NETCLIENT *c, NETCHUNK *chunk)
|
||||
{
|
||||
if(chunk->data_size >= NET_MAX_PAYLOAD)
|
||||
{
|
||||
dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(chunk->flags&NETSENDFLAG_CONNLESS)
|
||||
{
|
||||
/* send connectionless packet */
|
||||
send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
int f = 0;
|
||||
dbg_assert(chunk->client_id == 0, "errornous client id");
|
||||
|
||||
if(chunk->flags&NETSENDFLAG_VITAL)
|
||||
f = NET_CHUNKFLAG_VITAL;
|
||||
|
||||
conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data);
|
||||
|
||||
if(chunk->flags&NETSENDFLAG_FLUSH)
|
||||
conn_flush(&c->conn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netclient_state(NETCLIENT *c)
|
||||
{
|
||||
if(c->conn.state == NET_CONNSTATE_ONLINE)
|
||||
return NETSTATE_ONLINE;
|
||||
if(c->conn.state == NET_CONNSTATE_OFFLINE)
|
||||
return NETSTATE_OFFLINE;
|
||||
return NETSTATE_CONNECTING;
|
||||
}
|
||||
|
||||
int netclient_flush(NETCLIENT *c)
|
||||
{
|
||||
return conn_flush(&c->conn);
|
||||
}
|
||||
|
||||
int netclient_gotproblems(NETCLIENT *c)
|
||||
{
|
||||
if(time_get() - c->conn.last_recv_time > time_freq())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void netclient_stats(NETCLIENT *c, NETSTATS *stats)
|
||||
{
|
||||
*stats = c->conn.stats;
|
||||
}
|
||||
|
||||
const char *netclient_error_string(NETCLIENT *c)
|
||||
{
|
||||
return conn_error(&c->conn);
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
#include <base/system.h>
|
||||
#include <string.h>
|
||||
#include "e_config.h"
|
||||
#include "e_network_internal.h"
|
||||
|
||||
static void conn_reset_stats(NETCONNECTION *conn)
|
||||
{
|
||||
mem_zero(&conn->stats, sizeof(conn->stats));
|
||||
}
|
||||
|
||||
static void conn_reset(NETCONNECTION *conn)
|
||||
{
|
||||
conn->seq = 0;
|
||||
conn->ack = 0;
|
||||
conn->remote_closed = 0;
|
||||
|
||||
conn->state = NET_CONNSTATE_OFFLINE;
|
||||
conn->last_send_time = 0;
|
||||
conn->last_recv_time = 0;
|
||||
conn->last_update_time = 0;
|
||||
conn->token = -1;
|
||||
mem_zero(&conn->peeraddr, sizeof(conn->peeraddr));
|
||||
|
||||
conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0);
|
||||
|
||||
mem_zero(&conn->construct, sizeof(conn->construct));
|
||||
}
|
||||
|
||||
|
||||
const char *conn_error(NETCONNECTION *conn)
|
||||
{
|
||||
return conn->error_string;
|
||||
}
|
||||
|
||||
static void conn_set_error(NETCONNECTION *conn, const char *str)
|
||||
{
|
||||
str_copy(conn->error_string, str, sizeof(conn->error_string));
|
||||
}
|
||||
|
||||
void conn_init(NETCONNECTION *conn, NETSOCKET socket)
|
||||
{
|
||||
conn_reset(conn);
|
||||
conn_reset_stats(conn);
|
||||
conn->socket = socket;
|
||||
mem_zero(conn->error_string, sizeof(conn->error_string));
|
||||
}
|
||||
|
||||
|
||||
static void conn_ack(NETCONNECTION *conn, int ack)
|
||||
{
|
||||
|
||||
while(1)
|
||||
{
|
||||
NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
|
||||
if(!resend)
|
||||
break;
|
||||
|
||||
if(seq_in_backroom(resend->sequence, ack))
|
||||
ringbuf_popfirst(conn->buffer);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void conn_want_resend(NETCONNECTION *conn)
|
||||
{
|
||||
conn->construct.flags |= NET_PACKETFLAG_RESEND;
|
||||
}
|
||||
|
||||
int conn_flush(NETCONNECTION *conn)
|
||||
{
|
||||
int num_chunks = conn->construct.num_chunks;
|
||||
if(!num_chunks && !conn->construct.flags)
|
||||
return 0;
|
||||
|
||||
/* send of the packets */
|
||||
conn->construct.ack = conn->ack;
|
||||
send_packet(conn->socket, &conn->peeraddr, &conn->construct);
|
||||
|
||||
/* update send times */
|
||||
conn->last_send_time = time_get();
|
||||
|
||||
/* clear construct so we can start building a new package */
|
||||
mem_zero(&conn->construct, sizeof(conn->construct));
|
||||
return num_chunks;
|
||||
}
|
||||
|
||||
static int conn_queue_chunk_ex(NETCONNECTION *conn, int flags, int data_size, const void *data, int sequence)
|
||||
{
|
||||
unsigned char *chunk_data;
|
||||
|
||||
/* check if we have space for it, if not, flush the connection */
|
||||
if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data))
|
||||
conn_flush(conn);
|
||||
|
||||
/* pack all the data */
|
||||
chunk_data = &conn->construct.chunk_data[conn->construct.data_size];
|
||||
chunk_data = pack_chunk_header(chunk_data, flags, data_size, sequence);
|
||||
mem_copy(chunk_data, data, data_size);
|
||||
chunk_data += data_size;
|
||||
|
||||
/* */
|
||||
conn->construct.num_chunks++;
|
||||
conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data);
|
||||
|
||||
/* set packet flags aswell */
|
||||
|
||||
if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND))
|
||||
{
|
||||
/* save packet if we need to resend */
|
||||
NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size);
|
||||
if(resend)
|
||||
{
|
||||
resend->sequence = sequence;
|
||||
resend->flags = flags;
|
||||
resend->data_size = data_size;
|
||||
resend->data = (unsigned char *)(resend+1);
|
||||
resend->first_send_time = time_get();
|
||||
resend->last_send_time = resend->first_send_time;
|
||||
mem_copy(resend->data, data, data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* out of buffer */
|
||||
conn_disconnect(conn, "too weak connection (out of buffer)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data)
|
||||
{
|
||||
if(flags&NET_CHUNKFLAG_VITAL)
|
||||
conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE;
|
||||
return conn_queue_chunk_ex(conn, flags, data_size, data, conn->seq);
|
||||
}
|
||||
|
||||
|
||||
static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size)
|
||||
{
|
||||
/* send the control message */
|
||||
conn->last_send_time = time_get();
|
||||
send_controlmsg(conn->socket, &conn->peeraddr, conn->ack, controlmsg, extra, extra_size);
|
||||
}
|
||||
|
||||
static void conn_resend_chunk(NETCONNECTION *conn, NETCHUNKDATA *resend)
|
||||
{
|
||||
conn_queue_chunk_ex(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data, resend->sequence);
|
||||
resend->last_send_time = time_get();
|
||||
}
|
||||
|
||||
static void conn_resend(NETCONNECTION *conn)
|
||||
{
|
||||
int resend_count = 0;
|
||||
int first = 0, last = 0;
|
||||
void *item = ringbuf_first(conn->buffer);
|
||||
|
||||
while(item)
|
||||
{
|
||||
NETCHUNKDATA *resend = (NETCHUNKDATA *)item;
|
||||
|
||||
if(resend_count == 0)
|
||||
first = resend->sequence;
|
||||
last = resend->sequence;
|
||||
|
||||
conn_resend_chunk(conn, resend);
|
||||
item = ringbuf_next(conn->buffer, item);
|
||||
resend_count++;
|
||||
}
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("conn", "resent %d packets (%d to %d)", resend_count, first, last);
|
||||
}
|
||||
|
||||
int conn_connect(NETCONNECTION *conn, NETADDR *addr)
|
||||
{
|
||||
if(conn->state != NET_CONNSTATE_OFFLINE)
|
||||
return -1;
|
||||
|
||||
/* init connection */
|
||||
conn_reset(conn);
|
||||
conn->peeraddr = *addr;
|
||||
mem_zero(conn->error_string, sizeof(conn->error_string));
|
||||
conn->state = NET_CONNSTATE_CONNECT;
|
||||
conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_disconnect(NETCONNECTION *conn, const char *reason)
|
||||
{
|
||||
if(conn->state == NET_CONNSTATE_OFFLINE)
|
||||
return;
|
||||
|
||||
if(conn->remote_closed == 0)
|
||||
{
|
||||
if(reason)
|
||||
conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1);
|
||||
else
|
||||
conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0);
|
||||
|
||||
conn->error_string[0] = 0;
|
||||
if(reason)
|
||||
str_copy(conn->error_string, reason, sizeof(conn->error_string));
|
||||
}
|
||||
|
||||
conn_reset(conn);
|
||||
}
|
||||
|
||||
int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr)
|
||||
{
|
||||
int64 now = time_get();
|
||||
conn->last_recv_time = now;
|
||||
|
||||
/* check if resend is requested */
|
||||
if(packet->flags&NET_PACKETFLAG_RESEND)
|
||||
conn_resend(conn);
|
||||
|
||||
/* */
|
||||
if(packet->flags&NET_PACKETFLAG_CONTROL)
|
||||
{
|
||||
int ctrlmsg = packet->chunk_data[0];
|
||||
|
||||
if(ctrlmsg == NET_CTRLMSG_CLOSE)
|
||||
{
|
||||
conn->state = NET_CONNSTATE_ERROR;
|
||||
conn->remote_closed = 1;
|
||||
|
||||
if(packet->data_size)
|
||||
{
|
||||
/* make sure to sanitize the error string form the other party*/
|
||||
char str[128];
|
||||
if(packet->data_size < 128)
|
||||
str_copy(str, (char *)packet->chunk_data, packet->data_size);
|
||||
else
|
||||
str_copy(str, (char *)packet->chunk_data, 128);
|
||||
str_sanitize_strong(str);
|
||||
|
||||
/* set the error string */
|
||||
conn_set_error(conn, str);
|
||||
}
|
||||
else
|
||||
conn_set_error(conn, "no reason given");
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("conn", "closed reason='%s'", conn_error(conn));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(conn->state == NET_CONNSTATE_OFFLINE)
|
||||
{
|
||||
if(ctrlmsg == NET_CTRLMSG_CONNECT)
|
||||
{
|
||||
/* send response and init connection */
|
||||
conn_reset(conn);
|
||||
conn->state = NET_CONNSTATE_PENDING;
|
||||
conn->peeraddr = *addr;
|
||||
conn->last_send_time = now;
|
||||
conn->last_recv_time = now;
|
||||
conn->last_update_time = now;
|
||||
conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
|
||||
if(config.debug)
|
||||
dbg_msg("connection", "got connection, sending connect+accept");
|
||||
}
|
||||
}
|
||||
else if(conn->state == NET_CONNSTATE_CONNECT)
|
||||
{
|
||||
/* connection made */
|
||||
if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
|
||||
{
|
||||
conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0);
|
||||
conn->state = NET_CONNSTATE_ONLINE;
|
||||
if(config.debug)
|
||||
dbg_msg("connection", "got connect+accept, sending accept. connection online");
|
||||
}
|
||||
}
|
||||
else if(conn->state == NET_CONNSTATE_ONLINE)
|
||||
{
|
||||
/* connection made */
|
||||
/*
|
||||
if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT)
|
||||
{
|
||||
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(conn->state == NET_CONNSTATE_PENDING)
|
||||
{
|
||||
conn->state = NET_CONNSTATE_ONLINE;
|
||||
if(config.debug)
|
||||
dbg_msg("connection", "connecting online");
|
||||
}
|
||||
}
|
||||
|
||||
if(conn->state == NET_CONNSTATE_ONLINE)
|
||||
{
|
||||
|
||||
conn_ack(conn, packet->ack);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int conn_update(NETCONNECTION *conn)
|
||||
{
|
||||
int64 now = time_get();
|
||||
|
||||
if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR)
|
||||
return 0;
|
||||
|
||||
/* check for timeout */
|
||||
if(conn->state != NET_CONNSTATE_OFFLINE &&
|
||||
conn->state != NET_CONNSTATE_CONNECT &&
|
||||
(now-conn->last_recv_time) > time_freq()*10)
|
||||
{
|
||||
conn->state = NET_CONNSTATE_ERROR;
|
||||
conn_set_error(conn, "timeout");
|
||||
}
|
||||
|
||||
/* fix resends */
|
||||
if(ringbuf_first(conn->buffer))
|
||||
{
|
||||
NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer);
|
||||
|
||||
/* check if we have some really old stuff laying around and abort if not acked */
|
||||
if(now-resend->first_send_time > time_freq()*10)
|
||||
{
|
||||
conn->state = NET_CONNSTATE_ERROR;
|
||||
conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* resend packet if we havn't got it acked in 1 second */
|
||||
if(now-resend->last_send_time > time_freq())
|
||||
conn_resend_chunk(conn, resend);
|
||||
}
|
||||
}
|
||||
|
||||
/* send keep alives if nothing has happend for 250ms */
|
||||
if(conn->state == NET_CONNSTATE_ONLINE)
|
||||
{
|
||||
if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 500ms if needed */
|
||||
{
|
||||
int num_flushed_chunks = conn_flush(conn);
|
||||
if(num_flushed_chunks && config.debug)
|
||||
dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks);
|
||||
}
|
||||
|
||||
if(time_get()-conn->last_send_time > time_freq())
|
||||
conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0);
|
||||
}
|
||||
else if(conn->state == NET_CONNSTATE_CONNECT)
|
||||
{
|
||||
if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */
|
||||
conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0);
|
||||
}
|
||||
else if(conn->state == NET_CONNSTATE_PENDING)
|
||||
{
|
||||
if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */
|
||||
conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
#include <base/system.h>
|
||||
#include "e_network.h"
|
||||
#include "e_ringbuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
CURRENT:
|
||||
packet header: 3 bytes
|
||||
unsigned char flags_ack; // 4bit flags, 4bit ack
|
||||
unsigned char ack; // 8 bit ack
|
||||
unsigned char num_chunks; // 8 bit chunks
|
||||
|
||||
(unsigned char padding[3]) // 24 bit extra incase it's a connection less packet
|
||||
// this is to make sure that it's compatible with the
|
||||
// old protocol
|
||||
|
||||
chunk header: 2-3 bytes
|
||||
unsigned char flags_size; // 2bit flags, 6 bit size
|
||||
unsigned char size_seq; // 4bit size, 4bit seq
|
||||
(unsigned char seq;) // 8bit seq, if vital flag is set
|
||||
|
||||
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
NET_VERSION = 2,
|
||||
|
||||
NET_MAX_CHUNKSIZE = 1024,
|
||||
NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16,
|
||||
NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16,
|
||||
NET_MAX_CHUNKHEADERSIZE = 5,
|
||||
NET_PACKETHEADERSIZE = 3,
|
||||
NET_MAX_CLIENTS = 16,
|
||||
NET_MAX_SEQUENCE = 1<<10,
|
||||
NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1,
|
||||
|
||||
NET_CONNSTATE_OFFLINE=0,
|
||||
NET_CONNSTATE_CONNECT=1,
|
||||
NET_CONNSTATE_PENDING=2,
|
||||
NET_CONNSTATE_ONLINE=3,
|
||||
NET_CONNSTATE_ERROR=4,
|
||||
|
||||
NET_PACKETFLAG_CONTROL=1,
|
||||
NET_PACKETFLAG_CONNLESS=2,
|
||||
NET_PACKETFLAG_RESEND=4,
|
||||
NET_PACKETFLAG_COMPRESSION=8,
|
||||
|
||||
NET_CHUNKFLAG_VITAL=1,
|
||||
NET_CHUNKFLAG_RESEND=2,
|
||||
|
||||
NET_CTRLMSG_KEEPALIVE=0,
|
||||
NET_CTRLMSG_CONNECT=1,
|
||||
NET_CTRLMSG_CONNECTACCEPT=2,
|
||||
NET_CTRLMSG_ACCEPT=3,
|
||||
NET_CTRLMSG_CLOSE=4,
|
||||
|
||||
NET_SERVER_MAXBANS=1024,
|
||||
|
||||
NET_CONN_BUFFERSIZE=1024*16,
|
||||
|
||||
NET_ENUM_TERMINATOR
|
||||
};
|
||||
|
||||
|
||||
typedef struct NETPACKETCONSTRUCT
|
||||
{
|
||||
int flags;
|
||||
int ack;
|
||||
int num_chunks;
|
||||
int data_size;
|
||||
unsigned char chunk_data[NET_MAX_PAYLOAD];
|
||||
} NETPACKETCONSTRUCT;
|
||||
|
||||
typedef struct NETCHUNKHEADER
|
||||
{
|
||||
int flags;
|
||||
int size;
|
||||
int sequence;
|
||||
} NETCHUNKHEADER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int flags;
|
||||
int data_size;
|
||||
unsigned char *data;
|
||||
|
||||
int sequence;
|
||||
int64 last_send_time;
|
||||
int64 first_send_time;
|
||||
} NETCHUNKDATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short seq;
|
||||
unsigned short ack;
|
||||
unsigned state;
|
||||
|
||||
int token;
|
||||
int remote_closed;
|
||||
|
||||
RINGBUFFER *buffer;
|
||||
|
||||
int64 last_update_time;
|
||||
int64 last_recv_time;
|
||||
int64 last_send_time;
|
||||
|
||||
char error_string[256];
|
||||
|
||||
NETPACKETCONSTRUCT construct;
|
||||
|
||||
NETADDR peeraddr;
|
||||
NETSOCKET socket;
|
||||
NETSTATS stats;
|
||||
|
||||
char buffer_memory[NET_CONN_BUFFERSIZE];
|
||||
} NETCONNECTION;
|
||||
|
||||
typedef struct NETRECVINFO
|
||||
{
|
||||
NETADDR addr;
|
||||
NETCONNECTION *conn;
|
||||
int current_chunk;
|
||||
int client_id;
|
||||
int valid;
|
||||
NETPACKETCONSTRUCT data;
|
||||
unsigned char buffer[NET_MAX_PACKETSIZE];
|
||||
} NETRECVINFO;
|
||||
|
||||
/* */
|
||||
|
||||
/* connection functions */
|
||||
void conn_init(NETCONNECTION *conn, NETSOCKET socket);
|
||||
int conn_connect(NETCONNECTION *conn, NETADDR *addr);
|
||||
void conn_disconnect(NETCONNECTION *conn, const char *reason);
|
||||
int conn_update(NETCONNECTION *conn);
|
||||
int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr);
|
||||
int conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data);
|
||||
const char *conn_error(NETCONNECTION *conn);
|
||||
void conn_want_resend(NETCONNECTION *conn);
|
||||
int conn_flush(NETCONNECTION *conn);
|
||||
|
||||
/* recvinfo functions */
|
||||
void recvinfo_clear(NETRECVINFO *info);
|
||||
void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid);
|
||||
int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk);
|
||||
|
||||
/* misc helper functions */
|
||||
/* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */
|
||||
int seq_in_backroom(int seq, int ack);
|
||||
void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size);
|
||||
void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size);
|
||||
void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet);
|
||||
int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet);
|
||||
unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence);
|
||||
unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,491 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <base/system.h>
|
||||
#include "e_network.h"
|
||||
#include "e_network_internal.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NETCONNECTION conn;
|
||||
} NETSLOT;
|
||||
|
||||
typedef struct NETBAN
|
||||
{
|
||||
NETBANINFO info;
|
||||
|
||||
/* hash list */
|
||||
struct NETBAN *hashnext;
|
||||
struct NETBAN *hashprev;
|
||||
|
||||
/* used or free list */
|
||||
struct NETBAN *next;
|
||||
struct NETBAN *prev;
|
||||
} NETBAN;
|
||||
|
||||
#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \
|
||||
{ if(first) first->prev = object; \
|
||||
object->prev = NULL; \
|
||||
object->next = first; \
|
||||
first = object; }
|
||||
|
||||
#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \
|
||||
{ object->prev = after; \
|
||||
object->next = after->next; \
|
||||
after->next = object; \
|
||||
if(object->next) \
|
||||
object->next->prev = object; \
|
||||
}
|
||||
|
||||
#define MACRO_LIST_UNLINK(object, first, prev, next) \
|
||||
{ if(object->next) object->next->prev = object->prev; \
|
||||
if(object->prev) object->prev->next = object->next; \
|
||||
else first = object->next; \
|
||||
object->next = NULL; object->prev = NULL; }
|
||||
|
||||
#define MACRO_LIST_FIND(start, next, expression) \
|
||||
{ while(start && !(expression)) start = start->next; }
|
||||
|
||||
struct NETSERVER
|
||||
{
|
||||
NETSOCKET socket;
|
||||
NETSLOT slots[NET_MAX_CLIENTS];
|
||||
int max_clients;
|
||||
|
||||
NETBAN *bans[256];
|
||||
NETBAN banpool[NET_SERVER_MAXBANS];
|
||||
NETBAN *banpool_firstfree;
|
||||
NETBAN *banpool_firstused;
|
||||
|
||||
NETFUNC_NEWCLIENT new_client;
|
||||
NETFUNC_NEWCLIENT del_client;
|
||||
void *user_ptr;
|
||||
|
||||
NETRECVINFO recv;
|
||||
};
|
||||
|
||||
NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags)
|
||||
{
|
||||
int i;
|
||||
NETSERVER *server;
|
||||
NETSOCKET socket = net_udp_create(bindaddr);
|
||||
if(socket == NETSOCKET_INVALID)
|
||||
return 0;
|
||||
|
||||
server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
|
||||
mem_zero(server, sizeof(NETSERVER));
|
||||
server->socket = socket;
|
||||
server->max_clients = max_clients;
|
||||
if(server->max_clients > NET_MAX_CLIENTS)
|
||||
server->max_clients = NET_MAX_CLIENTS;
|
||||
if(server->max_clients < 1)
|
||||
server->max_clients = 1;
|
||||
|
||||
for(i = 0; i < NET_MAX_CLIENTS; i++)
|
||||
conn_init(&server->slots[i].conn, server->socket);
|
||||
|
||||
/* setup all pointers for bans */
|
||||
for(i = 1; i < NET_SERVER_MAXBANS-1; i++)
|
||||
{
|
||||
server->banpool[i].next = &server->banpool[i+1];
|
||||
server->banpool[i].prev = &server->banpool[i-1];
|
||||
}
|
||||
|
||||
server->banpool[0].next = &server->banpool[1];
|
||||
server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2];
|
||||
server->banpool_firstfree = &server->banpool[0];
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
|
||||
{
|
||||
s->new_client = new_client;
|
||||
s->del_client = del_client;
|
||||
s->user_ptr = user;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netserver_max_clients(NETSERVER *s)
|
||||
{
|
||||
return s->max_clients;
|
||||
}
|
||||
|
||||
int netserver_close(NETSERVER *s)
|
||||
{
|
||||
/* TODO: implement me */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netserver_drop(NETSERVER *s, int client_id, const char *reason)
|
||||
{
|
||||
/* TODO: insert lots of checks here */
|
||||
NETADDR addr;
|
||||
netserver_client_addr(s, client_id, &addr);
|
||||
|
||||
dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"",
|
||||
client_id,
|
||||
addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3],
|
||||
reason
|
||||
);
|
||||
conn_disconnect(&s->slots[client_id].conn, reason);
|
||||
|
||||
if(s->del_client)
|
||||
s->del_client(client_id, s->user_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info)
|
||||
{
|
||||
NETBAN *ban;
|
||||
for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--)
|
||||
{}
|
||||
|
||||
if(!ban)
|
||||
return 0;
|
||||
*info = ban->info;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int netserver_ban_num(NETSERVER *s)
|
||||
{
|
||||
int count = 0;
|
||||
NETBAN *ban;
|
||||
for(ban = s->banpool_firstused; ban; ban = ban->next)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
static void netserver_ban_remove_by_object(NETSERVER *s, NETBAN *ban)
|
||||
{
|
||||
int iphash = (ban->info.addr.ip[0]+ban->info.addr.ip[1]+ban->info.addr.ip[2]+ban->info.addr.ip[3])&0xff;
|
||||
dbg_msg("netserver", "removing ban on %d.%d.%d.%d",
|
||||
ban->info.addr.ip[0], ban->info.addr.ip[1], ban->info.addr.ip[2], ban->info.addr.ip[3]);
|
||||
MACRO_LIST_UNLINK(ban, s->banpool_firstused, prev, next);
|
||||
MACRO_LIST_UNLINK(ban, s->bans[iphash], hashprev, hashnext);
|
||||
MACRO_LIST_LINK_FIRST(ban, s->banpool_firstfree, prev, next);
|
||||
}
|
||||
|
||||
int netserver_ban_remove(NETSERVER *s, NETADDR addr)
|
||||
{
|
||||
int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
|
||||
NETBAN *ban = s->bans[iphash];
|
||||
|
||||
MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0);
|
||||
|
||||
if(ban)
|
||||
{
|
||||
netserver_ban_remove_by_object(s, ban);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds)
|
||||
{
|
||||
int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
|
||||
unsigned stamp = 0xffffffff;
|
||||
NETBAN *ban;
|
||||
|
||||
/* remove the port */
|
||||
addr.port = 0;
|
||||
|
||||
if(seconds)
|
||||
stamp = time_timestamp() + seconds;
|
||||
|
||||
/* search to see if it already exists */
|
||||
ban = s->bans[iphash];
|
||||
MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0);
|
||||
if(ban)
|
||||
{
|
||||
/* adjust the ban */
|
||||
ban->info.expires = stamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!s->banpool_firstfree)
|
||||
return -1;
|
||||
|
||||
/* fetch and clear the new ban */
|
||||
ban = s->banpool_firstfree;
|
||||
MACRO_LIST_UNLINK(ban, s->banpool_firstfree, prev, next);
|
||||
|
||||
/* setup the ban info */
|
||||
ban->info.expires = stamp;
|
||||
ban->info.addr = addr;
|
||||
|
||||
/* add it to the ban hash */
|
||||
MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext);
|
||||
|
||||
/* insert it into the used list */
|
||||
{
|
||||
if(s->banpool_firstused)
|
||||
{
|
||||
NETBAN *insert_after = s->banpool_firstused;
|
||||
MACRO_LIST_FIND(insert_after, next, stamp < insert_after->info.expires);
|
||||
|
||||
if(insert_after)
|
||||
insert_after = insert_after->prev;
|
||||
else
|
||||
{
|
||||
/* add to last */
|
||||
insert_after = s->banpool_firstused;
|
||||
while(insert_after->next)
|
||||
insert_after = insert_after->next;
|
||||
}
|
||||
|
||||
if(insert_after)
|
||||
{
|
||||
MACRO_LIST_LINK_AFTER(ban, insert_after, prev, next);
|
||||
}
|
||||
else
|
||||
{
|
||||
MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next);
|
||||
}
|
||||
}
|
||||
|
||||
/* drop banned clients */
|
||||
{
|
||||
char buf[128];
|
||||
int i;
|
||||
NETADDR banaddr;
|
||||
|
||||
if(seconds)
|
||||
str_format(buf, sizeof(buf), "you have been banned for %d minutes", seconds/60);
|
||||
else
|
||||
str_format(buf, sizeof(buf), "you have been banned for life");
|
||||
|
||||
for(i = 0; i < s->max_clients; i++)
|
||||
{
|
||||
banaddr = s->slots[i].conn.peeraddr;
|
||||
banaddr.port = 0;
|
||||
|
||||
if(net_addr_comp(&addr, &banaddr) == 0)
|
||||
netserver_drop(s, i, buf);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netserver_update(NETSERVER *s)
|
||||
{
|
||||
unsigned now = time_timestamp();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < s->max_clients; i++)
|
||||
{
|
||||
conn_update(&s->slots[i].conn);
|
||||
if(s->slots[i].conn.state == NET_CONNSTATE_ERROR)
|
||||
netserver_drop(s, i, conn_error(&s->slots[i].conn));
|
||||
}
|
||||
|
||||
/* remove expired bans */
|
||||
while(s->banpool_firstused && s->banpool_firstused->info.expires < now)
|
||||
{
|
||||
NETBAN *ban = s->banpool_firstused;
|
||||
netserver_ban_remove_by_object(s, ban);
|
||||
}
|
||||
|
||||
(void)now;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: chopp up this function into smaller working parts
|
||||
*/
|
||||
int netserver_recv(NETSERVER *s, NETCHUNK *chunk)
|
||||
{
|
||||
unsigned now = time_timestamp();
|
||||
|
||||
while(1)
|
||||
{
|
||||
NETADDR addr;
|
||||
int i, bytes, found;
|
||||
|
||||
/* check for a chunk */
|
||||
if(recvinfo_fetch_chunk(&s->recv, chunk))
|
||||
return 1;
|
||||
|
||||
/* TODO: empty the recvinfo */
|
||||
bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE);
|
||||
|
||||
/* no more packets for now */
|
||||
if(bytes <= 0)
|
||||
break;
|
||||
|
||||
if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0)
|
||||
{
|
||||
NETBAN *ban = 0;
|
||||
NETADDR banaddr = addr;
|
||||
int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff;
|
||||
found = 0;
|
||||
banaddr.port = 0;
|
||||
|
||||
/* search a ban */
|
||||
for(ban = s->bans[iphash]; ban; ban = ban->hashnext)
|
||||
{
|
||||
if(net_addr_comp(&ban->info.addr, &banaddr) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* check if we just should drop the packet */
|
||||
if(ban)
|
||||
{
|
||||
// banned, reply with a message
|
||||
char banstr[128];
|
||||
if(ban->info.expires)
|
||||
{
|
||||
int mins = ((ban->info.expires - now)+59)/60;
|
||||
if(mins == 1)
|
||||
str_format(banstr, sizeof(banstr), "banned for %d minute", mins);
|
||||
else
|
||||
str_format(banstr, sizeof(banstr), "banned for %d minutes", mins);
|
||||
}
|
||||
else
|
||||
str_format(banstr, sizeof(banstr), "banned for life");
|
||||
send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS)
|
||||
{
|
||||
chunk->flags = NETSENDFLAG_CONNLESS;
|
||||
chunk->client_id = -1;
|
||||
chunk->address = addr;
|
||||
chunk->data_size = s->recv.data.data_size;
|
||||
chunk->data = s->recv.data.chunk_data;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: check size here */
|
||||
if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT)
|
||||
{
|
||||
found = 0;
|
||||
|
||||
/* check if we already got this client */
|
||||
for(i = 0; i < s->max_clients; i++)
|
||||
{
|
||||
if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE &&
|
||||
net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
|
||||
{
|
||||
found = 1; /* silent ignore.. we got this client already */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* client that wants to connect */
|
||||
if(!found)
|
||||
{
|
||||
for(i = 0; i < s->max_clients; i++)
|
||||
{
|
||||
if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE)
|
||||
{
|
||||
found = 1;
|
||||
conn_feed(&s->slots[i].conn, &s->recv.data, &addr);
|
||||
if(s->new_client)
|
||||
s->new_client(i, s->user_ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
const char fullmsg[] = "server is full";
|
||||
send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* normal packet, find matching slot */
|
||||
for(i = 0; i < s->max_clients; i++)
|
||||
{
|
||||
if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0)
|
||||
{
|
||||
if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr))
|
||||
{
|
||||
if(s->recv.data.data_size)
|
||||
recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netserver_send(NETSERVER *s, NETCHUNK *chunk)
|
||||
{
|
||||
if(chunk->data_size >= NET_MAX_PAYLOAD)
|
||||
{
|
||||
dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(chunk->flags&NETSENDFLAG_CONNLESS)
|
||||
{
|
||||
/* send connectionless packet */
|
||||
send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
int f = 0;
|
||||
dbg_assert(chunk->client_id >= 0, "errornous client id");
|
||||
dbg_assert(chunk->client_id < s->max_clients, "errornous client id");
|
||||
|
||||
if(chunk->flags&NETSENDFLAG_VITAL)
|
||||
f = NET_CHUNKFLAG_VITAL;
|
||||
|
||||
if(conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data) == 0)
|
||||
{
|
||||
if(chunk->flags&NETSENDFLAG_FLUSH)
|
||||
conn_flush(&s->slots[chunk->client_id].conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
netserver_drop(s, chunk->client_id, "error sending data");
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void netserver_stats(NETSERVER *s, NETSTATS *stats)
|
||||
{
|
||||
int num_stats = sizeof(NETSTATS)/sizeof(int);
|
||||
int *istats = (int *)stats;
|
||||
int c, i;
|
||||
|
||||
mem_zero(stats, sizeof(NETSTATS));
|
||||
|
||||
for(c = 0; c < s->max_clients; c++)
|
||||
{
|
||||
if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE)
|
||||
{
|
||||
int *sstats = (int *)(&(s->slots[c].conn.stats));
|
||||
for(i = 0; i < num_stats; i++)
|
||||
istats[i] += sstats[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NETSOCKET netserver_socket(NETSERVER *s)
|
||||
{
|
||||
return s->socket;
|
||||
}
|
||||
|
||||
|
||||
int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr)
|
||||
{
|
||||
*addr = s->slots[client_id].conn.peeraddr;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <stdlib.h> /* rand() */
|
||||
#include <base/system.h>
|
||||
|
||||
#include "e_packer.h"
|
||||
#include "e_compression.h"
|
||||
#include "e_engine.h"
|
||||
#include "e_config.h"
|
||||
|
||||
/* useful for debugging */
|
||||
#if 0
|
||||
#define packing_error(p) p->error = 1; dbg_break()
|
||||
#else
|
||||
#define packing_error(p) p->error = 1
|
||||
#endif
|
||||
|
||||
static int stress_get_int()
|
||||
{
|
||||
static const int nasty[] = {-1, 0, 1, 66000, -66000, (-1<<31), 0x7fffffff};
|
||||
if(rand()&1)
|
||||
return rand();
|
||||
return nasty[rand()%6];
|
||||
}
|
||||
|
||||
static const char *stress_get_string(int *size)
|
||||
{
|
||||
static char noise[1024];
|
||||
int i;
|
||||
int s;
|
||||
s = (rand()%1024)-1;
|
||||
for(i = 0; i < s; i++)
|
||||
noise[i] = (rand()%254)+1;
|
||||
noise[s] = 0;
|
||||
if(size)
|
||||
*size = s;
|
||||
return noise;
|
||||
}
|
||||
|
||||
|
||||
static int stress_prob(float probability)
|
||||
{
|
||||
if(!config.dbg_stress_network)
|
||||
return 0;
|
||||
if(rand()/(float)RAND_MAX < probability)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void packer_reset(PACKER *p)
|
||||
{
|
||||
p->error = 0;
|
||||
p->current = p->buffer;
|
||||
p->end = p->current + PACKER_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
void packer_add_int(PACKER *p, int i)
|
||||
{
|
||||
if(p->error)
|
||||
return;
|
||||
|
||||
if(stress_prob(0.025f))
|
||||
i = stress_get_int();
|
||||
|
||||
/* make sure that we have space enough */
|
||||
if(p->end - p->current < 6)
|
||||
{
|
||||
dbg_break();
|
||||
p->error = 1;
|
||||
}
|
||||
else
|
||||
p->current = vint_pack(p->current, i);
|
||||
}
|
||||
|
||||
void packer_add_string(PACKER *p, const char *str, int limit)
|
||||
{
|
||||
if(p->error)
|
||||
return;
|
||||
|
||||
if(stress_prob(0.1f))
|
||||
{
|
||||
str = stress_get_string(0);
|
||||
limit = 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
if(limit > 0)
|
||||
{
|
||||
while(*str && limit != 0)
|
||||
{
|
||||
*p->current++ = *str++;
|
||||
limit--;
|
||||
|
||||
if(p->current >= p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p->current++ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
*p->current++ = *str++;
|
||||
|
||||
if(p->current >= p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p->current++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void packer_add_raw(PACKER *p, const unsigned char *data, int size)
|
||||
{
|
||||
if(p->error)
|
||||
return;
|
||||
|
||||
if(p->current+size >= p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
return;
|
||||
}
|
||||
|
||||
while(size)
|
||||
{
|
||||
*p->current++ = *data++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
int packer_size(PACKER *p)
|
||||
{
|
||||
return (const unsigned char *)p->current-(const unsigned char *)p->buffer;
|
||||
}
|
||||
|
||||
const unsigned char *packer_data(PACKER *p)
|
||||
{
|
||||
return (const unsigned char *)p->buffer;
|
||||
}
|
||||
|
||||
void unpacker_reset(UNPACKER *p, const unsigned char *data, int size)
|
||||
{
|
||||
p->error = 0;
|
||||
p->start = data;
|
||||
p->end = p->start + size;
|
||||
p->current = p->start;
|
||||
}
|
||||
|
||||
int unpacker_get_int(UNPACKER *p)
|
||||
{
|
||||
int i;
|
||||
if(p->error)
|
||||
return 0;
|
||||
if(p->current >= p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->current = vint_unpack(p->current, &i);
|
||||
if(p->current > p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
const char *unpacker_get_string(UNPACKER *p)
|
||||
{
|
||||
char *ptr;
|
||||
if(p->error || p->current >= p->end)
|
||||
return "";
|
||||
|
||||
ptr = (char *)p->current;
|
||||
while(*p->current) /* skip the string */
|
||||
{
|
||||
p->current++;
|
||||
if(p->current == p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
p->current++;
|
||||
|
||||
/* sanitize all strings */
|
||||
str_sanitize(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const unsigned char *unpacker_get_raw(UNPACKER *p, int size)
|
||||
{
|
||||
const unsigned char *ptr = p->current;
|
||||
if(p->error)
|
||||
return 0;
|
||||
|
||||
/* check for nasty sizes */
|
||||
if(size < 0 || p->current+size > p->end)
|
||||
{
|
||||
packing_error(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* "unpack" the data */
|
||||
p->current += size;
|
||||
return ptr;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
PACKER_BUFFER_SIZE=1024*2
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
unsigned char buffer[PACKER_BUFFER_SIZE];
|
||||
unsigned char *current;
|
||||
unsigned char *end;
|
||||
int error;
|
||||
} PACKER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const unsigned char *current;
|
||||
const unsigned char *start;
|
||||
const unsigned char *end;
|
||||
int error;
|
||||
} UNPACKER;
|
||||
|
||||
void packer_reset(PACKER *p);
|
||||
void packer_add_int(PACKER *p, int i);
|
||||
void packer_add_string(PACKER *p, const char *str, int limit);
|
||||
void packer_add_raw(PACKER *p, const unsigned char *data, int size);
|
||||
int packer_size(PACKER *p);
|
||||
const unsigned char *packer_data(PACKER *p);
|
||||
|
||||
void unpacker_reset(UNPACKER *p, const unsigned char *data, int size);
|
||||
int unpacker_get_int(UNPACKER *p);
|
||||
const char *unpacker_get_string(UNPACKER *p);
|
||||
const unsigned char *unpacker_get_raw(UNPACKER *p, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
|
||||
/*
|
||||
Connection diagram - How the initilization works.
|
||||
|
||||
Client -> INFO -> Server
|
||||
Contains version info, name, and some other info.
|
||||
|
||||
Client <- MAP <- Server
|
||||
Contains current map.
|
||||
|
||||
Client -> READY -> Server
|
||||
The client has loaded the map and is ready to go,
|
||||
but the mod needs to send it's information aswell.
|
||||
modc_connected is called on the client and
|
||||
mods_connected is called on the server.
|
||||
The client should call client_entergame when the
|
||||
mod has done it's initilization.
|
||||
|
||||
Client -> ENTERGAME -> Server
|
||||
Tells the server to start sending snapshots.
|
||||
client_entergame and server_client_enter is called.
|
||||
*/
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
NETMSG_NULL=0,
|
||||
|
||||
/* the first thing sent by the client
|
||||
contains the version info for the client */
|
||||
NETMSG_INFO=1,
|
||||
|
||||
/* sent by server */
|
||||
NETMSG_MAP_CHANGE, /* sent when client should switch map */
|
||||
NETMSG_MAP_DATA, /* map transfer, contains a chunk of the map file */
|
||||
NETMSG_SNAP, /* normal snapshot, multiple parts */
|
||||
NETMSG_SNAPEMPTY, /* empty snapshot */
|
||||
NETMSG_SNAPSINGLE, /* ? */
|
||||
NETMSG_SNAPSMALL, /* */
|
||||
NETMSG_INPUTTIMING, /* reports how off the input was */
|
||||
NETMSG_RCON_AUTH_STATUS,/* result of the authentication */
|
||||
NETMSG_RCON_LINE, /* line that should be printed to the remote console */
|
||||
|
||||
NETMSG_AUTH_CHALLANGE, /* */
|
||||
NETMSG_AUTH_RESULT, /* */
|
||||
|
||||
/* sent by client */
|
||||
NETMSG_READY, /* */
|
||||
NETMSG_ENTERGAME,
|
||||
NETMSG_INPUT, /* contains the inputdata from the client */
|
||||
NETMSG_RCON_CMD, /* */
|
||||
NETMSG_RCON_AUTH, /* */
|
||||
NETMSG_REQUEST_MAP_DATA,/* */
|
||||
|
||||
NETMSG_AUTH_START, /* */
|
||||
NETMSG_AUTH_RESPONSE, /* */
|
||||
|
||||
/* sent by both */
|
||||
NETMSG_PING,
|
||||
NETMSG_PING_REPLY,
|
||||
NETMSG_ERROR
|
||||
};
|
||||
|
||||
|
||||
/* this should be revised */
|
||||
enum
|
||||
{
|
||||
MAX_CLANNAME_LENGTH=32,
|
||||
MAX_INPUT_SIZE=128,
|
||||
MAX_SNAPSHOT_PACKSIZE=900
|
||||
};
|
||||
@@ -1,362 +0,0 @@
|
||||
#include <base/system.h>
|
||||
|
||||
#include "e_ringbuffer.h"
|
||||
|
||||
typedef struct RBITEM
|
||||
{
|
||||
struct RBITEM *prev;
|
||||
struct RBITEM *next;
|
||||
int free;
|
||||
int size;
|
||||
} RBITEM;
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
struct RINGBUFFER
|
||||
{
|
||||
RBITEM *produce;
|
||||
RBITEM *consume;
|
||||
|
||||
RBITEM *first;
|
||||
RBITEM *last;
|
||||
void *memory;
|
||||
int size;
|
||||
int flags;
|
||||
};
|
||||
|
||||
RINGBUFFER *ringbuf_init(void *memory, int size, int flags)
|
||||
{
|
||||
RINGBUFFER *rb = (RINGBUFFER *)memory;
|
||||
mem_zero(memory, size);
|
||||
|
||||
rb->memory = rb+1;
|
||||
rb->size = (size-sizeof(RINGBUFFER))/sizeof(RBITEM)*sizeof(RBITEM);
|
||||
rb->first = (RBITEM *)rb->memory;
|
||||
rb->first->free = 1;
|
||||
rb->first->size = rb->size;
|
||||
rb->last = rb->first;
|
||||
rb->produce = rb->first;
|
||||
rb->consume = rb->first;
|
||||
|
||||
rb->flags = flags;
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
static RBITEM *ringbuf_nextblock(RINGBUFFER *rb, RBITEM *item)
|
||||
{
|
||||
if(item->next)
|
||||
return item->next;
|
||||
return rb->first;
|
||||
}
|
||||
|
||||
static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item)
|
||||
{
|
||||
if(item->prev)
|
||||
return item->prev;
|
||||
return rb->last;
|
||||
}
|
||||
|
||||
static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item)
|
||||
{
|
||||
/* make sure that this block and previous block is free */
|
||||
if(!item->free || !item->prev || !item->prev->free)
|
||||
return item;
|
||||
|
||||
/* merge the blocks */
|
||||
item->prev->size += item->size;
|
||||
item->prev->next = item->next;
|
||||
|
||||
/* fixup pointers */
|
||||
if(item->next)
|
||||
item->next->prev = item->prev;
|
||||
else
|
||||
rb->last = item->prev;
|
||||
|
||||
if(item == rb->produce)
|
||||
rb->produce = item->prev;
|
||||
|
||||
if(item == rb->consume)
|
||||
rb->consume = item->prev;
|
||||
|
||||
/* return the current block */
|
||||
return item->prev;
|
||||
}
|
||||
|
||||
int ringbuf_popfirst(RINGBUFFER *rb)
|
||||
{
|
||||
if(rb->consume->free)
|
||||
return 0;
|
||||
|
||||
/* set the free flag */
|
||||
rb->consume->free = 1;
|
||||
|
||||
/* previous block is also free, merge them */
|
||||
rb->consume = ringbuf_mergeback(rb, rb->consume);
|
||||
|
||||
/* advance the consume pointer */
|
||||
rb->consume = ringbuf_nextblock(rb, rb->consume);
|
||||
while(rb->consume->free && rb->consume != rb->produce)
|
||||
{
|
||||
rb->consume = ringbuf_mergeback(rb, rb->consume);
|
||||
rb->consume = ringbuf_nextblock(rb, rb->consume);
|
||||
}
|
||||
|
||||
/* in the case that we have catched up with the produce pointer */
|
||||
/* we might stand on a free block so merge em */
|
||||
ringbuf_mergeback(rb, rb->consume);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *ringbuf_allocate(RINGBUFFER *rb, int size)
|
||||
{
|
||||
int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM);
|
||||
RBITEM *block = 0;
|
||||
|
||||
/* check if we even can fit this block */
|
||||
if(wanted_size > rb->size)
|
||||
return 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* check for space */
|
||||
if(rb->produce->free)
|
||||
{
|
||||
if(rb->produce->size >= wanted_size)
|
||||
block = rb->produce;
|
||||
else
|
||||
{
|
||||
/* wrap around to try to find a block */
|
||||
if(rb->first->free && rb->first->size >= wanted_size)
|
||||
block = rb->first;
|
||||
}
|
||||
}
|
||||
|
||||
if(block)
|
||||
break;
|
||||
else
|
||||
{
|
||||
/* we have no block, check our policy and see what todo */
|
||||
if(rb->flags&RINGBUF_FLAG_RECYCLE)
|
||||
{
|
||||
if(!ringbuf_popfirst(rb))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* okey, we have our block */
|
||||
|
||||
/* split the block if needed */
|
||||
if(block->size > wanted_size)
|
||||
{
|
||||
RBITEM *new_item = (RBITEM *)((char *)block + wanted_size);
|
||||
new_item->prev = block;
|
||||
new_item->next = block->next;
|
||||
if(new_item->next)
|
||||
new_item->next->prev = new_item;
|
||||
block->next = new_item;
|
||||
|
||||
new_item->free = 1;
|
||||
new_item->size = block->size - wanted_size;
|
||||
block->size = wanted_size;
|
||||
|
||||
if(!new_item->next)
|
||||
rb->last = new_item;
|
||||
}
|
||||
|
||||
|
||||
/* set next block */
|
||||
rb->produce = ringbuf_nextblock(rb, block);
|
||||
|
||||
/* set as used and return the item pointer */
|
||||
block->free = 0;
|
||||
return block+1;
|
||||
}
|
||||
|
||||
void *ringbuf_prev(RINGBUFFER *rb, void *current)
|
||||
{
|
||||
RBITEM *item = ((RBITEM *)current) - 1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
item = ringbuf_prevblock(rb, item);
|
||||
if(item == rb->produce)
|
||||
return 0;
|
||||
if(!item->free)
|
||||
return item+1;
|
||||
}
|
||||
}
|
||||
|
||||
void *ringbuf_next(RINGBUFFER *rb, void *current)
|
||||
{
|
||||
RBITEM *item = ((RBITEM *)current) - 1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
item = ringbuf_nextblock(rb, item);
|
||||
if(item == rb->produce)
|
||||
return 0;
|
||||
if(!item->free)
|
||||
return item+1;
|
||||
}
|
||||
}
|
||||
|
||||
void *ringbuf_first(RINGBUFFER *rb)
|
||||
{
|
||||
if(rb->consume->free)
|
||||
return 0;
|
||||
return rb->consume+1;
|
||||
}
|
||||
|
||||
void *ringbuf_last(RINGBUFFER *rb)
|
||||
{
|
||||
if(!rb->produce->free)
|
||||
return rb->produce+1;
|
||||
return ringbuf_prev(rb, rb->produce+1);
|
||||
}
|
||||
|
||||
|
||||
/* debugging and testing stuff */
|
||||
|
||||
static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg)
|
||||
{
|
||||
RBITEM *cur = rb->first;
|
||||
|
||||
dbg_msg("ringbuf", "-- dumping --");
|
||||
|
||||
while(cur)
|
||||
{
|
||||
char flags[4] = " ";
|
||||
if(cur->free)
|
||||
flags[0] = 'F';
|
||||
if(cur == rb->consume)
|
||||
flags[1] = '>';
|
||||
if(cur == rb->produce)
|
||||
flags[2] = '<';
|
||||
dbg_msg("ringbuf", "%s %d", flags, cur->size);
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
dbg_msg("ringbuf", "-- --");
|
||||
|
||||
if(msg)
|
||||
dbg_assert(0, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ringbuf_validate(RINGBUFFER *rb)
|
||||
{
|
||||
RBITEM *prev = 0;
|
||||
RBITEM *cur = rb->first;
|
||||
int freechunks = 0;
|
||||
int got_consume = 0;
|
||||
int got_produce = 0;
|
||||
|
||||
while(cur)
|
||||
{
|
||||
|
||||
if(cur->free)
|
||||
freechunks++;
|
||||
|
||||
if(freechunks > 2) ringbuf_debugdump(rb, "too many free chunks");
|
||||
if(prev && prev->free && cur->free) ringbuf_debugdump(rb, "two free chunks next to each other");
|
||||
if(cur == rb->consume) got_consume = 1;
|
||||
if(cur == rb->produce) got_produce = 1;
|
||||
|
||||
dbg_assert(cur->prev == prev, "prev pointers doesn't match");
|
||||
dbg_assert(!prev || prev->next == cur, "next pointers doesn't match");
|
||||
dbg_assert(cur->next || cur == rb->last, "last isn't last");
|
||||
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if(!got_consume) ringbuf_debugdump(rb, "consume pointer isn't pointing at a valid block");
|
||||
if(!got_produce) ringbuf_debugdump(rb, "produce pointer isn't pointing at a valid block");
|
||||
}
|
||||
|
||||
int ringbuf_test()
|
||||
{
|
||||
char buffer[256];
|
||||
RINGBUFFER *rb;
|
||||
int i, s, k, m;
|
||||
int count;
|
||||
int testcount = 0;
|
||||
|
||||
void *item;
|
||||
int before;
|
||||
|
||||
|
||||
for(k = 100; k < sizeof(buffer); k++)
|
||||
{
|
||||
if((k%10) == 0)
|
||||
dbg_msg("ringbuf", "testing at %d", k);
|
||||
rb = ringbuf_init(buffer, k, 0);
|
||||
count = 0;
|
||||
|
||||
for(s = 1; s < sizeof(buffer); s++)
|
||||
{
|
||||
for(i = 0; i < k*8; i++, testcount++)
|
||||
{
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
before = m;
|
||||
|
||||
if(ringbuf_allocate(rb, s))
|
||||
{
|
||||
count++;
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
if(before+1 != m) ringbuf_debugdump(rb, "alloc error");
|
||||
if(count != m) ringbuf_debugdump(rb, "count error");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ringbuf_popfirst(rb))
|
||||
{
|
||||
count--;
|
||||
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
|
||||
if(count != m) ringbuf_debugdump(rb, "count error");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove an item every 10 */
|
||||
if((i%10) == 0)
|
||||
{
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
before = m;
|
||||
|
||||
if(ringbuf_popfirst(rb))
|
||||
{
|
||||
count--;
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
if(before-1 != m) dbg_msg("", "popping error %d %d", before, m);
|
||||
dbg_assert(count == m, "count error");
|
||||
}
|
||||
}
|
||||
|
||||
/* count items */
|
||||
for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++);
|
||||
if(m != count) ringbuf_debugdump(rb, "wrong number of items during forward count");
|
||||
|
||||
for(m = 0, item = ringbuf_last(rb); item; item = ringbuf_prev(rb, item), m++);
|
||||
if(m != count) ringbuf_debugdump(rb, "wrong number of items during backward count");
|
||||
|
||||
ringbuf_validate(rb);
|
||||
}
|
||||
|
||||
/* empty the ring buffer */
|
||||
while(ringbuf_first(rb))
|
||||
ringbuf_popfirst(rb);
|
||||
ringbuf_validate(rb);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef _RINGBUFFER_H
|
||||
#define _RINGBUFFER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct RINGBUFFER RINGBUFFER;
|
||||
|
||||
enum
|
||||
{
|
||||
/* Will start to destroy items to try to fit the next one */
|
||||
RINGBUF_FLAG_RECYCLE=1
|
||||
};
|
||||
|
||||
RINGBUFFER *ringbuf_init(void *memory, int size, int flags);
|
||||
void ringbuf_clear(RINGBUFFER *rb);
|
||||
void *ringbuf_allocate(RINGBUFFER *rb, int size);
|
||||
|
||||
void *ringbuf_prev(RINGBUFFER *rb, void *current);
|
||||
void *ringbuf_next(RINGBUFFER *rb, void *current);
|
||||
void *ringbuf_first(RINGBUFFER *rb);
|
||||
void *ringbuf_last(RINGBUFFER *rb);
|
||||
|
||||
int ringbuf_popfirst(RINGBUFFER *rb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_SERVER_INTERFACE_H
|
||||
#define ENGINE_SERVER_INTERFACE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "e_if_other.h"
|
||||
#include "e_if_server.h"
|
||||
#include "e_if_msg.h"
|
||||
#include "e_if_mods.h"
|
||||
|
||||
#include "e_console.h" /* TODO: clean this up*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,604 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <stdlib.h>
|
||||
#include "e_snapshot.h"
|
||||
#include "e_engine.h"
|
||||
#include "e_compression.h"
|
||||
#include "e_common_interface.h"
|
||||
|
||||
|
||||
/* TODO: strange arbitrary number */
|
||||
static short item_sizes[64] = {0};
|
||||
|
||||
void snap_set_staticsize(int itemtype, int size)
|
||||
{
|
||||
item_sizes[itemtype] = size;
|
||||
}
|
||||
|
||||
int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); }
|
||||
int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; }
|
||||
int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; }
|
||||
int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; }
|
||||
|
||||
int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); }
|
||||
char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); }
|
||||
|
||||
SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index)
|
||||
{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); }
|
||||
|
||||
int snapshot_get_item_datasize(SNAPSHOT *snap, int index)
|
||||
{
|
||||
if(index == snap->num_items-1)
|
||||
return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM);
|
||||
return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM);
|
||||
}
|
||||
|
||||
int snapshot_get_item_index(SNAPSHOT *snap, int key)
|
||||
{
|
||||
/* TODO: OPT: this should not be a linear search. very bad */
|
||||
int i;
|
||||
for(i = 0; i < snap->num_items; i++)
|
||||
{
|
||||
if(snapitem_key(snapshot_get_item(snap, i)) == key)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
typedef struct
|
||||
{
|
||||
int num;
|
||||
int keys[64];
|
||||
int index[64];
|
||||
} ITEMLIST;
|
||||
static ITEMLIST sorted[256];
|
||||
|
||||
static int snapshot_generate_hash(SNAPSHOT *snap)
|
||||
{
|
||||
int i, key, hashid;
|
||||
|
||||
for(i = 0; i < 256; i++)
|
||||
sorted[i].num = 0;
|
||||
|
||||
for(i = 0; i < snap->num_items; i++)
|
||||
{
|
||||
key = snapitem_key(snapshot_get_item(snap, i));
|
||||
hashid = ((key>>8)&0xf0) | (key&0xf);
|
||||
if(sorted[hashid].num != 64)
|
||||
{
|
||||
sorted[hashid].index[sorted[hashid].num] = i;
|
||||
sorted[hashid].keys[sorted[hashid].num] = key;
|
||||
sorted[hashid].num++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snapshot_get_item_index_hashed(SNAPSHOT *snap, int key)
|
||||
{
|
||||
int hashid = ((key>>8)&0xf0) | (key&0xf);
|
||||
int i;
|
||||
for(i = 0; i < sorted[hashid].num; i++)
|
||||
{
|
||||
if(sorted[hashid].keys[i] == key)
|
||||
return sorted[hashid].index[i];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int num_deleted_items;
|
||||
int num_update_items;
|
||||
int num_temp_items; /* needed? */
|
||||
int data[1];
|
||||
|
||||
/*
|
||||
char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; }
|
||||
|
||||
int deleted_item(int index) { return offsets[index]; }
|
||||
item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); }
|
||||
item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); }
|
||||
*/
|
||||
} SNAPSHOT_DELTA;
|
||||
|
||||
|
||||
static const int MAX_ITEMS = 512;
|
||||
static SNAPSHOT_DELTA empty = {0,0,0,{0}};
|
||||
|
||||
void *snapshot_empty_delta()
|
||||
{
|
||||
return ∅
|
||||
}
|
||||
|
||||
int snapshot_crc(SNAPSHOT *snap)
|
||||
{
|
||||
int crc = 0;
|
||||
int i, b;
|
||||
SNAPSHOT_ITEM *item;
|
||||
int size;
|
||||
|
||||
for(i = 0; i < snap->num_items; i++)
|
||||
{
|
||||
item = snapshot_get_item(snap, i);
|
||||
size = snapshot_get_item_datasize(snap, i);
|
||||
|
||||
for(b = 0; b < size/4; b++)
|
||||
crc += snapitem_data(item)[b];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void snapshot_debug_dump(SNAPSHOT *snap)
|
||||
{
|
||||
int size, i, b;
|
||||
SNAPSHOT_ITEM *item;
|
||||
|
||||
dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items);
|
||||
for(i = 0; i < snap->num_items; i++)
|
||||
{
|
||||
item = snapshot_get_item(snap, i);
|
||||
size = snapshot_get_item_datasize(snap, i);
|
||||
dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item));
|
||||
for(b = 0; b < size/4; b++)
|
||||
dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]);
|
||||
}
|
||||
}
|
||||
|
||||
static int diff_item(int *past, int *current, int *out, int size)
|
||||
{
|
||||
int needed = 0;
|
||||
while(size)
|
||||
{
|
||||
*out = *current-*past;
|
||||
needed |= *out;
|
||||
out++;
|
||||
past++;
|
||||
current++;
|
||||
size--;
|
||||
}
|
||||
|
||||
return needed;
|
||||
}
|
||||
|
||||
int snapshot_data_rate[0xffff] = {0};
|
||||
int snapshot_data_updates[0xffff] = {0};
|
||||
static int snapshot_current = 0;
|
||||
|
||||
static void undiff_item(int *past, int *diff, int *out, int size)
|
||||
{
|
||||
while(size)
|
||||
{
|
||||
*out = *past+*diff;
|
||||
|
||||
if(*diff == 0)
|
||||
snapshot_data_rate[snapshot_current] += 1;
|
||||
else
|
||||
{
|
||||
unsigned char buf[16];
|
||||
unsigned char *end = vint_pack(buf, *diff);
|
||||
snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8;
|
||||
}
|
||||
|
||||
out++;
|
||||
past++;
|
||||
diff++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* TODO: OPT: this should be made much faster */
|
||||
int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata)
|
||||
{
|
||||
static PERFORMACE_INFO hash_scope = {"hash", 0};
|
||||
SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata;
|
||||
int *data = (int *)delta->data;
|
||||
int i, itemsize, pastindex;
|
||||
SNAPSHOT_ITEM *fromitem;
|
||||
SNAPSHOT_ITEM *curitem;
|
||||
SNAPSHOT_ITEM *pastitem;
|
||||
int count = 0;
|
||||
int size_count = 0;
|
||||
|
||||
delta->num_deleted_items = 0;
|
||||
delta->num_update_items = 0;
|
||||
delta->num_temp_items = 0;
|
||||
|
||||
perf_start(&hash_scope);
|
||||
snapshot_generate_hash(to);
|
||||
perf_end();
|
||||
|
||||
/* pack deleted stuff */
|
||||
{
|
||||
static PERFORMACE_INFO scope = {"delete", 0};
|
||||
perf_start(&scope);
|
||||
|
||||
for(i = 0; i < from->num_items; i++)
|
||||
{
|
||||
fromitem = snapshot_get_item(from, i);
|
||||
if(snapshot_get_item_index_hashed(to, (snapitem_key(fromitem))) == -1)
|
||||
{
|
||||
/* deleted */
|
||||
delta->num_deleted_items++;
|
||||
*data = snapitem_key(fromitem);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
perf_end();
|
||||
}
|
||||
|
||||
perf_start(&hash_scope);
|
||||
snapshot_generate_hash(from);
|
||||
perf_end();
|
||||
|
||||
/* pack updated stuff */
|
||||
{
|
||||
static PERFORMACE_INFO scope = {"update", 0};
|
||||
int pastindecies[1024];
|
||||
perf_start(&scope);
|
||||
|
||||
/* fetch previous indices */
|
||||
/* we do this as a separate pass because it helps the cache */
|
||||
{
|
||||
static PERFORMACE_INFO scope = {"find", 0};
|
||||
perf_start(&scope);
|
||||
for(i = 0; i < to->num_items; i++)
|
||||
{
|
||||
curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */
|
||||
pastindecies[i] = snapshot_get_item_index_hashed(from, snapitem_key(curitem)); /* O(n) .. O(n^n)*/
|
||||
}
|
||||
perf_end();
|
||||
}
|
||||
|
||||
for(i = 0; i < to->num_items; i++)
|
||||
{
|
||||
/* do delta */
|
||||
itemsize = snapshot_get_item_datasize(to, i); /* O(1) .. O(n) */
|
||||
curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */
|
||||
pastindex = pastindecies[i];
|
||||
|
||||
if(pastindex != -1)
|
||||
{
|
||||
static PERFORMACE_INFO scope = {"diff", 0};
|
||||
int *item_data_dst = data+3;
|
||||
perf_start(&scope);
|
||||
|
||||
pastitem = snapshot_get_item(from, pastindex);
|
||||
|
||||
if(item_sizes[snapitem_type(curitem)])
|
||||
item_data_dst = data+2;
|
||||
|
||||
if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), item_data_dst, itemsize/4))
|
||||
{
|
||||
|
||||
*data++ = snapitem_type(curitem);
|
||||
*data++ = snapitem_id(curitem);
|
||||
if(!item_sizes[snapitem_type(curitem)])
|
||||
*data++ = itemsize/4;
|
||||
data += itemsize/4;
|
||||
delta->num_update_items++;
|
||||
}
|
||||
perf_end();
|
||||
}
|
||||
else
|
||||
{
|
||||
static PERFORMACE_INFO scope = {"copy", 0};
|
||||
perf_start(&scope);
|
||||
|
||||
*data++ = snapitem_type(curitem);
|
||||
*data++ = snapitem_id(curitem);
|
||||
if(!item_sizes[snapitem_type(curitem)])
|
||||
*data++ = itemsize/4;
|
||||
|
||||
mem_copy(data, snapitem_data(curitem), itemsize);
|
||||
size_count += itemsize;
|
||||
data += itemsize/4;
|
||||
delta->num_update_items++;
|
||||
count++;
|
||||
|
||||
perf_end();
|
||||
}
|
||||
}
|
||||
|
||||
perf_end();
|
||||
}
|
||||
|
||||
if(0)
|
||||
{
|
||||
dbg_msg("snapshot", "%d %d %d",
|
||||
delta->num_deleted_items,
|
||||
delta->num_update_items,
|
||||
delta->num_temp_items);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: pack temp stuff
|
||||
|
||||
// finish
|
||||
//mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int));
|
||||
//mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int));
|
||||
//mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int));
|
||||
//mem_copy(delta->data_start(), data, data_size);
|
||||
//delta->data_size = data_size;
|
||||
* */
|
||||
|
||||
if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items)
|
||||
return 0;
|
||||
|
||||
return (int)((char*)data-(char*)dstdata);
|
||||
}
|
||||
|
||||
static int range_check(void *end, void *ptr, int size)
|
||||
{
|
||||
if((const char *)ptr + size > (const char *)end)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size)
|
||||
{
|
||||
SNAPBUILD builder;
|
||||
SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata;
|
||||
int *data = (int *)delta->data;
|
||||
int *end = (int *)(((char *)srcdata + data_size));
|
||||
|
||||
SNAPSHOT_ITEM *fromitem;
|
||||
int i, d, keep, itemsize;
|
||||
int *deleted;
|
||||
int id, type, key;
|
||||
int fromindex;
|
||||
int *newdata;
|
||||
|
||||
snapbuild_init(&builder);
|
||||
|
||||
/* unpack deleted stuff */
|
||||
deleted = data;
|
||||
data += delta->num_deleted_items;
|
||||
if(data > end)
|
||||
return -1;
|
||||
|
||||
/* copy all non deleted stuff */
|
||||
for(i = 0; i < from->num_items; i++)
|
||||
{
|
||||
/* dbg_assert(0, "fail!"); */
|
||||
fromitem = snapshot_get_item(from, i);
|
||||
itemsize = snapshot_get_item_datasize(from, i);
|
||||
keep = 1;
|
||||
for(d = 0; d < delta->num_deleted_items; d++)
|
||||
{
|
||||
if(deleted[d] == snapitem_key(fromitem))
|
||||
{
|
||||
keep = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(keep)
|
||||
{
|
||||
/* keep it */
|
||||
mem_copy(
|
||||
snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize),
|
||||
snapitem_data(fromitem), itemsize);
|
||||
}
|
||||
}
|
||||
|
||||
/* unpack updated stuff */
|
||||
for(i = 0; i < delta->num_update_items; i++)
|
||||
{
|
||||
if(data+2 > end)
|
||||
return -1;
|
||||
|
||||
type = *data++;
|
||||
id = *data++;
|
||||
if(item_sizes[type])
|
||||
itemsize = item_sizes[type];
|
||||
else
|
||||
{
|
||||
if(data+1 > end)
|
||||
return -2;
|
||||
itemsize = (*data++) * 4;
|
||||
}
|
||||
snapshot_current = type;
|
||||
|
||||
if(range_check(end, data, itemsize) || itemsize < 0) return -3;
|
||||
|
||||
key = (type<<16)|id;
|
||||
|
||||
/* create the item if needed */
|
||||
newdata = snapbuild_get_item_data(&builder, key);
|
||||
if(!newdata)
|
||||
newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize);
|
||||
|
||||
/*if(range_check(end, newdata, itemsize)) return -4;*/
|
||||
|
||||
fromindex = snapshot_get_item_index(from, key);
|
||||
if(fromindex != -1)
|
||||
{
|
||||
/* we got an update so we need to apply the diff */
|
||||
undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4);
|
||||
snapshot_data_updates[snapshot_current]++;
|
||||
}
|
||||
else /* no previous, just copy the data */
|
||||
{
|
||||
mem_copy(newdata, data, itemsize);
|
||||
snapshot_data_rate[snapshot_current] += itemsize*8;
|
||||
snapshot_data_updates[snapshot_current]++;
|
||||
}
|
||||
|
||||
data += itemsize/4;
|
||||
}
|
||||
|
||||
/* finish up */
|
||||
return snapbuild_finish(&builder, to);
|
||||
}
|
||||
|
||||
/* SNAPSTORAGE */
|
||||
|
||||
void snapstorage_init(SNAPSTORAGE *ss)
|
||||
{
|
||||
ss->first = 0;
|
||||
}
|
||||
|
||||
void snapstorage_purge_all(SNAPSTORAGE *ss)
|
||||
{
|
||||
SNAPSTORAGE_HOLDER *h = ss->first;
|
||||
SNAPSTORAGE_HOLDER *next;
|
||||
|
||||
while(h)
|
||||
{
|
||||
next = h->next;
|
||||
mem_free(h);
|
||||
h = next;
|
||||
}
|
||||
|
||||
/* no more snapshots in storage */
|
||||
ss->first = 0;
|
||||
ss->last = 0;
|
||||
}
|
||||
|
||||
void snapstorage_purge_until(SNAPSTORAGE *ss, int tick)
|
||||
{
|
||||
SNAPSTORAGE_HOLDER *next;
|
||||
SNAPSTORAGE_HOLDER *h = ss->first;
|
||||
|
||||
while(h)
|
||||
{
|
||||
next = h->next;
|
||||
if(h->tick >= tick)
|
||||
return; /* no more to remove */
|
||||
mem_free(h);
|
||||
|
||||
/* did we come to the end of the list? */
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
ss->first = next;
|
||||
next->prev = 0x0;
|
||||
|
||||
h = next;
|
||||
}
|
||||
|
||||
/* no more snapshots in storage */
|
||||
ss->first = 0;
|
||||
ss->last = 0;
|
||||
}
|
||||
|
||||
void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt)
|
||||
{
|
||||
/* allocate memory for holder + snapshot_data */
|
||||
SNAPSTORAGE_HOLDER *h;
|
||||
int total_size = sizeof(SNAPSTORAGE_HOLDER)+data_size;
|
||||
|
||||
if(create_alt)
|
||||
total_size += data_size;
|
||||
|
||||
h = (SNAPSTORAGE_HOLDER *)mem_alloc(total_size, 1);
|
||||
|
||||
/* set data */
|
||||
h->tick = tick;
|
||||
h->tagtime = tagtime;
|
||||
h->snap_size = data_size;
|
||||
h->snap = (SNAPSHOT*)(h+1);
|
||||
mem_copy(h->snap, data, data_size);
|
||||
|
||||
if(create_alt) /* create alternative if wanted */
|
||||
{
|
||||
h->alt_snap = (SNAPSHOT*)(((char *)h->snap) + data_size);
|
||||
mem_copy(h->alt_snap, data, data_size);
|
||||
}
|
||||
else
|
||||
h->alt_snap = 0;
|
||||
|
||||
|
||||
/* link */
|
||||
h->next = 0;
|
||||
h->prev = ss->last;
|
||||
if(ss->last)
|
||||
ss->last->next = h;
|
||||
else
|
||||
ss->first = h;
|
||||
ss->last = h;
|
||||
}
|
||||
|
||||
int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data)
|
||||
{
|
||||
SNAPSTORAGE_HOLDER *h = ss->first;
|
||||
|
||||
while(h)
|
||||
{
|
||||
if(h->tick == tick)
|
||||
{
|
||||
if(tagtime)
|
||||
*tagtime = h->tagtime;
|
||||
if(data)
|
||||
*data = h->snap;
|
||||
if(alt_data)
|
||||
*alt_data = h->alt_snap;
|
||||
return h->snap_size;
|
||||
}
|
||||
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* SNAPBUILD */
|
||||
|
||||
void snapbuild_init(SNAPBUILD *sb)
|
||||
{
|
||||
sb->data_size = 0;
|
||||
sb->num_items = 0;
|
||||
}
|
||||
|
||||
SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index)
|
||||
{
|
||||
return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]);
|
||||
}
|
||||
|
||||
int *snapbuild_get_item_data(SNAPBUILD *sb, int key)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < sb->num_items; i++)
|
||||
{
|
||||
if(snapitem_key(snapbuild_get_item(sb, i)) == key)
|
||||
return (int *)snapitem_data(snapbuild_get_item(sb, i));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snapbuild_finish(SNAPBUILD *sb, void *snapdata)
|
||||
{
|
||||
/* flattern and make the snapshot */
|
||||
SNAPSHOT *snap = (SNAPSHOT *)snapdata;
|
||||
int offset_size = sizeof(int)*sb->num_items;
|
||||
snap->data_size = sb->data_size;
|
||||
snap->num_items = sb->num_items;
|
||||
mem_copy(snapshot_offsets(snap), sb->offsets, offset_size);
|
||||
mem_copy(snapshot_datastart(snap), sb->data, sb->data_size);
|
||||
return sizeof(SNAPSHOT) + offset_size + sb->data_size;
|
||||
}
|
||||
|
||||
void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size)
|
||||
{
|
||||
SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size);
|
||||
|
||||
/*if(stress_prob(0.01f))
|
||||
{
|
||||
size += ((rand()%5) - 2)*4;
|
||||
if(size < 0)
|
||||
size = 0;
|
||||
}*/
|
||||
|
||||
mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size);
|
||||
obj->type_and_id = (type<<16)|id;
|
||||
sb->offsets[sb->num_items] = sb->data_size;
|
||||
sb->data_size += sizeof(SNAPSHOT_ITEM) + size;
|
||||
sb->num_items++;
|
||||
|
||||
dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data");
|
||||
dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items");
|
||||
|
||||
return snapitem_data(obj);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef ENGINE_SNAPSHOT_H
|
||||
#define ENGINE_SNAPSHOT_H
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
/* SNAPSHOT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_SNAPSHOT_SIZE=64*1024
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type_and_id;
|
||||
} SNAPSHOT_ITEM;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int data_size;
|
||||
int num_items;
|
||||
} SNAPSHOT;
|
||||
|
||||
int *snapitem_data(SNAPSHOT_ITEM *item);
|
||||
int snapitem_type(SNAPSHOT_ITEM *item);
|
||||
int snapitem_id(SNAPSHOT_ITEM *item);
|
||||
int snapitem_key(SNAPSHOT_ITEM *item);
|
||||
|
||||
int *snapshot_offsets(SNAPSHOT *snap);
|
||||
char *snapshot_datastart(SNAPSHOT *snap);
|
||||
|
||||
SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index);
|
||||
int snapshot_get_item_datasize(SNAPSHOT *snap, int index);
|
||||
int snapshot_get_item_index(SNAPSHOT *snap, int key);
|
||||
|
||||
void *snapshot_empty_delta();
|
||||
int snapshot_crc(SNAPSHOT *snap);
|
||||
void snapshot_debug_dump(SNAPSHOT *snap);
|
||||
int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data);
|
||||
int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size);
|
||||
|
||||
/* SNAPSTORAGE */
|
||||
|
||||
typedef struct SNAPSTORAGE_HOLDER_t
|
||||
{
|
||||
struct SNAPSTORAGE_HOLDER_t *prev;
|
||||
struct SNAPSTORAGE_HOLDER_t *next;
|
||||
|
||||
int64 tagtime;
|
||||
int tick;
|
||||
|
||||
int snap_size;
|
||||
SNAPSHOT *snap;
|
||||
SNAPSHOT *alt_snap;
|
||||
} SNAPSTORAGE_HOLDER;
|
||||
|
||||
typedef struct SNAPSTORAGE_t
|
||||
{
|
||||
SNAPSTORAGE_HOLDER *first;
|
||||
SNAPSTORAGE_HOLDER *last;
|
||||
} SNAPSTORAGE;
|
||||
|
||||
void snapstorage_init(SNAPSTORAGE *ss);
|
||||
void snapstorage_purge_all(SNAPSTORAGE *ss);
|
||||
void snapstorage_purge_until(SNAPSTORAGE *ss, int tick);
|
||||
void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt);
|
||||
int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data);
|
||||
|
||||
/* SNAPBUILD */
|
||||
|
||||
enum
|
||||
{
|
||||
SNAPBUILD_MAX_ITEMS = 1024*2
|
||||
};
|
||||
|
||||
typedef struct SNAPBUILD
|
||||
{
|
||||
char data[MAX_SNAPSHOT_SIZE];
|
||||
int data_size;
|
||||
|
||||
int offsets[SNAPBUILD_MAX_ITEMS];
|
||||
int num_items;
|
||||
} SNAPBUILD;
|
||||
|
||||
void snapbuild_init(SNAPBUILD *sb);
|
||||
SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index);
|
||||
int *snapbuild_get_item_data(SNAPBUILD *sb, int key);
|
||||
int snapbuild_finish(SNAPBUILD *sb, void *snapdata);
|
||||
void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ENGINE_SNAPSHOT_H */
|
||||
@@ -1,5 +0,0 @@
|
||||
The source code under this directory are external libraries
|
||||
with their own licences. The source you find here is stripped
|
||||
of unnessesary information. Please visit their websites for
|
||||
more information and official releases.
|
||||
|
||||
@@ -1,877 +0,0 @@
|
||||
/* pnglite.c - pnglite library
|
||||
For conditions of distribution and use, see copyright notice in pnglite.h
|
||||
*/
|
||||
#define DO_CRC_CHECKS 1
|
||||
#define USE_ZLIB 1
|
||||
|
||||
#if USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#else
|
||||
#include "zlite.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "pnglite.h"
|
||||
|
||||
|
||||
|
||||
static png_alloc_t png_alloc;
|
||||
static png_free_t png_free;
|
||||
|
||||
static size_t file_read(png_t* png, void* out, size_t size, size_t numel)
|
||||
{
|
||||
size_t result;
|
||||
if(png->read_fun)
|
||||
{
|
||||
result = png->read_fun(out, size, numel, png->user_pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!out)
|
||||
{
|
||||
result = fseek(png->user_pointer, (long)(size*numel), SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = fread(out, size, numel, png->user_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t file_write(png_t* png, void* p, size_t size, size_t numel)
|
||||
{
|
||||
size_t result;
|
||||
|
||||
if(png->write_fun)
|
||||
{
|
||||
result = png->write_fun(p, size, numel, png->user_pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = fwrite(p, size, numel, png->user_pointer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int file_read_ul(png_t* png, unsigned *out)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if(file_read(png, buf, 1, 4) != 4)
|
||||
return PNG_FILE_ERROR;
|
||||
|
||||
*out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int file_write_ul(png_t* png, unsigned in)
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
buf[0] = (in>>24) & 0xff;
|
||||
buf[1] = (in>>16) & 0xff;
|
||||
buf[2] = (in>>8) & 0xff;
|
||||
buf[3] = (in) & 0xff;
|
||||
|
||||
if(file_write(png, buf, 1, 4) != 4)
|
||||
return PNG_FILE_ERROR;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static unsigned get_ul(unsigned char* buf)
|
||||
{
|
||||
unsigned result;
|
||||
unsigned char foo[4];
|
||||
|
||||
memcpy(foo, buf, 4);
|
||||
|
||||
result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned set_ul(unsigned char* buf, unsigned in)
|
||||
{
|
||||
buf[0] = (in>>24) & 0xff;
|
||||
buf[1] = (in>>16) & 0xff;
|
||||
buf[2] = (in>>8) & 0xff;
|
||||
buf[3] = (in) & 0xff;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
int png_init(png_alloc_t pngalloc, png_free_t pngfree)
|
||||
{
|
||||
if(pngalloc)
|
||||
png_alloc = pngalloc;
|
||||
else
|
||||
png_alloc = &malloc;
|
||||
|
||||
if(pngfree)
|
||||
png_free = pngfree;
|
||||
else
|
||||
png_free = &free;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_get_bpp(png_t* png)
|
||||
{
|
||||
int bpp;
|
||||
|
||||
switch(png->color_type)
|
||||
{
|
||||
case PNG_GREYSCALE:
|
||||
bpp = 1; break;
|
||||
case PNG_TRUECOLOR:
|
||||
bpp = 3; break;
|
||||
case PNG_INDEXED:
|
||||
bpp = 1; break;
|
||||
case PNG_GREYSCALE_ALPHA:
|
||||
bpp = 2; break;
|
||||
case PNG_TRUECOLOR_ALPHA:
|
||||
bpp = 4; break;
|
||||
default:
|
||||
return PNG_FILE_ERROR;
|
||||
}
|
||||
|
||||
bpp *= png->depth/8;
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
static int png_read_ihdr(png_t* png)
|
||||
{
|
||||
unsigned length;
|
||||
#if DO_CRC_CHECKS
|
||||
unsigned orig_crc;
|
||||
unsigned calc_crc;
|
||||
#endif
|
||||
unsigned char ihdr[13+4]; /* length should be 13, make room for type (IHDR) */
|
||||
|
||||
file_read_ul(png, &length);
|
||||
|
||||
if(length != 13)
|
||||
{
|
||||
printf("%d\n", length);
|
||||
return PNG_CRC_ERROR;
|
||||
}
|
||||
|
||||
if(file_read(png, ihdr, 1, 13+4) != 13+4)
|
||||
return PNG_EOF_ERROR;
|
||||
#if DO_CRC_CHECKS
|
||||
file_read_ul(png, &orig_crc);
|
||||
|
||||
calc_crc = crc32(0L, 0, 0);
|
||||
calc_crc = crc32(calc_crc, ihdr, 13+4);
|
||||
|
||||
if(orig_crc != calc_crc)
|
||||
return PNG_CRC_ERROR;
|
||||
#else
|
||||
file_read_ul(png);
|
||||
#endif
|
||||
|
||||
png->width = get_ul(ihdr+4);
|
||||
png->height = get_ul(ihdr+8);
|
||||
png->depth = ihdr[12];
|
||||
png->color_type = ihdr[13];
|
||||
png->compression_method = ihdr[14];
|
||||
png->filter_method = ihdr[15];
|
||||
png->interlace_method = ihdr[16];
|
||||
|
||||
if(png->color_type == PNG_INDEXED)
|
||||
return PNG_NOT_SUPPORTED;
|
||||
|
||||
if(png->depth != 8 && png->depth != 16)
|
||||
return PNG_NOT_SUPPORTED;
|
||||
|
||||
if(png->interlace_method)
|
||||
return PNG_NOT_SUPPORTED;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_write_ihdr(png_t* png)
|
||||
{
|
||||
unsigned char ihdr[13+4];
|
||||
unsigned char *p = ihdr;
|
||||
unsigned crc;
|
||||
|
||||
file_write(png, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 1, 8);
|
||||
|
||||
file_write_ul(png, 13);
|
||||
|
||||
*p = 'I'; p++;
|
||||
*p = 'H'; p++;
|
||||
*p = 'D'; p++;
|
||||
*p = 'R'; p++;
|
||||
set_ul(p, png->width); p+=4;
|
||||
set_ul(p, png->height); p+=4;
|
||||
*p = png->depth; p++;
|
||||
*p = png->color_type; p++;
|
||||
*p = 0; p++;
|
||||
*p = 0; p++;
|
||||
*p = 0; p++;
|
||||
|
||||
file_write(png, ihdr, 1, 13+4);
|
||||
|
||||
crc = crc32(0L, 0, 0);
|
||||
crc = crc32(crc, ihdr, 13+4);
|
||||
|
||||
file_write_ul(png, crc);
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
void png_print_info(png_t* png)
|
||||
{
|
||||
printf("PNG INFO:\n");
|
||||
printf("\twidth:\t\t%d\n", png->width);
|
||||
printf("\theight:\t\t%d\n", png->height);
|
||||
printf("\tdepth:\t\t%d\n", png->depth);
|
||||
printf("\tcolor:\t\t");
|
||||
|
||||
switch(png->color_type)
|
||||
{
|
||||
case PNG_GREYSCALE: printf("greyscale\n"); break;
|
||||
case PNG_TRUECOLOR: printf("truecolor\n"); break;
|
||||
case PNG_INDEXED: printf("palette\n"); break;
|
||||
case PNG_GREYSCALE_ALPHA: printf("greyscale with alpha\n"); break;
|
||||
case PNG_TRUECOLOR_ALPHA: printf("truecolor with alpha\n"); break;
|
||||
default: printf("unknown, this is not good\n"); break;
|
||||
}
|
||||
|
||||
printf("\tcompression:\t%s\n", png->compression_method?"unknown, this is not good":"inflate/deflate");
|
||||
printf("\tfilter:\t\t%s\n", png->filter_method?"unknown, this is not good":"adaptive");
|
||||
printf("\tinterlace:\t%s\n", png->interlace_method?"interlace":"no interlace");
|
||||
}
|
||||
|
||||
int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer)
|
||||
{
|
||||
char header[8];
|
||||
int result;
|
||||
|
||||
png->read_fun = read_fun;
|
||||
png->write_fun = 0;
|
||||
png->user_pointer = user_pointer;
|
||||
|
||||
if(!read_fun && !user_pointer)
|
||||
return PNG_WRONG_ARGUMENTS;
|
||||
|
||||
if(file_read(png, header, 1, 8) != 8)
|
||||
return PNG_EOF_ERROR;
|
||||
|
||||
if(memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0)
|
||||
return PNG_HEADER_ERROR;
|
||||
|
||||
result = png_read_ihdr(png);
|
||||
|
||||
png->bpp = (unsigned char)png_get_bpp(png);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer)
|
||||
{
|
||||
png->write_fun = write_fun;
|
||||
png->read_fun = 0;
|
||||
png->user_pointer = user_pointer;
|
||||
|
||||
if(!write_fun && !user_pointer)
|
||||
return PNG_WRONG_ARGUMENTS;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer)
|
||||
{
|
||||
return png_open_read(png, read_fun, user_pointer);
|
||||
}
|
||||
|
||||
int png_open_file_read(png_t *png, const char* filename)
|
||||
{
|
||||
FILE* fp = fopen(filename, "rb");
|
||||
|
||||
if(!fp)
|
||||
return PNG_FILE_ERROR;
|
||||
|
||||
return png_open_read(png, 0, fp);
|
||||
}
|
||||
|
||||
int png_open_file_write(png_t *png, const char* filename)
|
||||
{
|
||||
FILE* fp = fopen(filename, "wb");
|
||||
|
||||
if(!fp)
|
||||
return PNG_FILE_ERROR;
|
||||
|
||||
return png_open_write(png, 0, fp);
|
||||
}
|
||||
|
||||
int png_open_file(png_t *png, const char* filename)
|
||||
{
|
||||
return png_open_file_read(png, filename);
|
||||
}
|
||||
|
||||
int png_close_file(png_t* png)
|
||||
{
|
||||
fclose(png->user_pointer);
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_init_deflate(png_t* png, unsigned char* data, int datalen)
|
||||
{
|
||||
z_stream *stream;
|
||||
png->zs = png_alloc(sizeof(z_stream));
|
||||
|
||||
stream = png->zs;
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
memset(stream, 0, sizeof(z_stream));
|
||||
|
||||
if(deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK)
|
||||
return PNG_ZLIB_ERROR;
|
||||
|
||||
stream->next_in = data;
|
||||
stream->avail_in = datalen;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_init_inflate(png_t* png)
|
||||
{
|
||||
#if USE_ZLIB
|
||||
z_stream *stream;
|
||||
png->zs = png_alloc(sizeof(z_stream));
|
||||
#else
|
||||
zl_stream *stream;
|
||||
png->zs = png_alloc(sizeof(zl_stream));
|
||||
#endif
|
||||
|
||||
stream = png->zs;
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
|
||||
|
||||
#if USE_ZLIB
|
||||
memset(stream, 0, sizeof(z_stream));
|
||||
if(inflateInit(stream) != Z_OK)
|
||||
return PNG_ZLIB_ERROR;
|
||||
#else
|
||||
memset(stream, 0, sizeof(zl_stream));
|
||||
if(z_inflateInit(stream) != Z_OK)
|
||||
return PNG_ZLIB_ERROR;
|
||||
#endif
|
||||
|
||||
stream->next_out = png->png_data;
|
||||
stream->avail_out = png->png_datalen;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_end_deflate(png_t* png)
|
||||
{
|
||||
z_stream *stream = png->zs;
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
deflateEnd(stream);
|
||||
|
||||
png_free(png->zs);
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_end_inflate(png_t* png)
|
||||
{
|
||||
#if USE_ZLIB
|
||||
z_stream *stream = png->zs;
|
||||
#else
|
||||
zl_stream *stream = png->zs;
|
||||
#endif
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
#if USE_ZLIB
|
||||
if(inflateEnd(stream) != Z_OK)
|
||||
#else
|
||||
if(z_inflateEnd(stream) != Z_OK)
|
||||
#endif
|
||||
{
|
||||
printf("ZLIB says: %s\n", stream->msg);
|
||||
return PNG_ZLIB_ERROR;
|
||||
}
|
||||
|
||||
png_free(png->zs);
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_inflate(png_t* png, char* data, int len)
|
||||
{
|
||||
int result;
|
||||
#if USE_ZLIB
|
||||
z_stream *stream = png->zs;
|
||||
#else
|
||||
zl_stream *stream = png->zs;
|
||||
#endif
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
stream->next_in = (unsigned char*)data;
|
||||
stream->avail_in = len;
|
||||
|
||||
#if USE_ZLIB
|
||||
result = inflate(stream, Z_SYNC_FLUSH);
|
||||
#else
|
||||
result = z_inflate(stream);
|
||||
#endif
|
||||
|
||||
if(result != Z_STREAM_END && result != Z_OK)
|
||||
{
|
||||
printf("%s\n", stream->msg);
|
||||
return PNG_ZLIB_ERROR;
|
||||
}
|
||||
|
||||
if(stream->avail_in != 0)
|
||||
return PNG_ZLIB_ERROR;
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_deflate(png_t* png, char* outdata, int outlen, int *outwritten)
|
||||
{
|
||||
int result;
|
||||
|
||||
z_stream *stream = png->zs;
|
||||
|
||||
|
||||
if(!stream)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
stream->next_out = (unsigned char*)outdata;
|
||||
stream->avail_out = outlen;
|
||||
|
||||
result = deflate(stream, Z_SYNC_FLUSH);
|
||||
|
||||
*outwritten = outlen - stream->avail_out;
|
||||
|
||||
if(result != Z_STREAM_END && result != Z_OK)
|
||||
{
|
||||
printf("%s\n", stream->msg);
|
||||
return PNG_ZLIB_ERROR;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int png_write_idats(png_t* png, unsigned char* data)
|
||||
{
|
||||
unsigned char *chunk;
|
||||
unsigned long written;
|
||||
unsigned long crc;
|
||||
unsigned size = png->width * png->height * png->bpp + png->height;
|
||||
|
||||
(void)png_init_deflate;
|
||||
(void)png_end_deflate;
|
||||
(void)png_deflate;
|
||||
|
||||
chunk = png_alloc(size);
|
||||
memcpy(chunk, "IDAT", 4);
|
||||
|
||||
written = size;
|
||||
compress(chunk+4, &written, data, size);
|
||||
|
||||
crc = crc32(0L, Z_NULL, 0);
|
||||
crc = crc32(crc, chunk, written+4);
|
||||
set_ul(chunk+written+4, crc);
|
||||
file_write_ul(png, written);
|
||||
file_write(png, chunk, 1, written+8);
|
||||
png_free(chunk);
|
||||
|
||||
file_write_ul(png, 0);
|
||||
file_write(png, "IEND", 1, 4);
|
||||
crc = crc32(0L, (const unsigned char *)"IEND", 4);
|
||||
file_write_ul(png, crc);
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_read_idat(png_t* png, unsigned firstlen)
|
||||
{
|
||||
unsigned type = 0;
|
||||
char *chunk;
|
||||
int result;
|
||||
unsigned length = firstlen;
|
||||
unsigned old_len = length;
|
||||
|
||||
#if DO_CRC_CHECKS
|
||||
unsigned orig_crc;
|
||||
unsigned calc_crc;
|
||||
#endif
|
||||
|
||||
chunk = png_alloc(firstlen);
|
||||
|
||||
result = png_init_inflate(png);
|
||||
|
||||
if(result != PNG_NO_ERROR)
|
||||
{
|
||||
png_end_inflate(png);
|
||||
png_free(chunk);
|
||||
return result;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if(file_read(png, chunk, 1, length) != length)
|
||||
{
|
||||
png_end_inflate(png);
|
||||
png_free(chunk);
|
||||
return PNG_FILE_ERROR;
|
||||
}
|
||||
|
||||
#if DO_CRC_CHECKS
|
||||
calc_crc = crc32(0L, Z_NULL, 0);
|
||||
calc_crc = crc32(calc_crc, (unsigned char*)"IDAT", 4);
|
||||
calc_crc = crc32(calc_crc, (unsigned char*)chunk, length);
|
||||
|
||||
file_read_ul(png, &orig_crc);
|
||||
|
||||
if(orig_crc != calc_crc)
|
||||
{
|
||||
result = PNG_CRC_ERROR;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
file_read_ul(png);
|
||||
#endif
|
||||
|
||||
result = png_inflate(png, chunk, length);
|
||||
|
||||
if(result != PNG_NO_ERROR) break;
|
||||
|
||||
file_read_ul(png, &length);
|
||||
|
||||
if(length > old_len)
|
||||
{
|
||||
png_free(chunk);
|
||||
chunk = png_alloc(length);
|
||||
old_len = length;
|
||||
}
|
||||
|
||||
if(file_read(png, &type, 1, 4) != 4)
|
||||
{
|
||||
result = PNG_FILE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
}while(type == *(unsigned int*)"IDAT");
|
||||
|
||||
if(type == *(unsigned int*)"IEND")
|
||||
result = PNG_DONE;
|
||||
|
||||
png_free(chunk);
|
||||
png_end_inflate(png);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int png_process_chunk(png_t* png)
|
||||
{
|
||||
int result = PNG_NO_ERROR;
|
||||
unsigned type;
|
||||
unsigned length;
|
||||
|
||||
file_read_ul(png, &length);
|
||||
|
||||
if(file_read(png, &type, 1, 4) != 4)
|
||||
return PNG_FILE_ERROR;
|
||||
|
||||
if(type == *(unsigned int*)"IDAT") /* if we found an idat, all other idats should be followed with no other chunks in between */
|
||||
{
|
||||
png->png_datalen = png->width * png->height * png->bpp + png->height;
|
||||
png->png_data = png_alloc(png->png_datalen);
|
||||
|
||||
if(!png->png_data)
|
||||
return PNG_MEMORY_ERROR;
|
||||
|
||||
return png_read_idat(png, length);
|
||||
}
|
||||
else if(type == *(unsigned int*)"IEND")
|
||||
{
|
||||
return PNG_DONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_read(png, 0, 1, length + 4); /* unknown chunk */
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void png_filter_sub(int stride, unsigned char* in, unsigned char* out, int len)
|
||||
{
|
||||
int i;
|
||||
unsigned char a = 0;
|
||||
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if(i >= stride)
|
||||
a = out[i - stride];
|
||||
|
||||
out[i] = in[i] + a;
|
||||
}
|
||||
}
|
||||
|
||||
static void png_filter_up(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(prev_line)
|
||||
{
|
||||
for(i = 0; i < len; i++)
|
||||
out[i] = in[i] + prev_line[i];
|
||||
}
|
||||
else
|
||||
memcpy(out, in, len);
|
||||
}
|
||||
|
||||
static void png_filter_average(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
|
||||
{
|
||||
int i;
|
||||
unsigned char a = 0;
|
||||
unsigned char b = 0;
|
||||
unsigned int sum = 0;
|
||||
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if(prev_line)
|
||||
b = prev_line[i];
|
||||
|
||||
if(i >= stride)
|
||||
a = out[i - stride];
|
||||
|
||||
sum = a;
|
||||
sum += b;
|
||||
|
||||
out[i] = (char)(in[i] + sum/2);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char png_paeth(unsigned char a, unsigned char b, unsigned char c)
|
||||
{
|
||||
int p = (int)a + b - c;
|
||||
int pa = abs(p - a);
|
||||
int pb = abs(p - b);
|
||||
int pc = abs(p - c);
|
||||
|
||||
int pr;
|
||||
|
||||
if(pa <= pb && pa <= pc)
|
||||
pr = a;
|
||||
else if(pb <= pc)
|
||||
pr = b;
|
||||
else
|
||||
pr = c;
|
||||
|
||||
return (char)pr;
|
||||
}
|
||||
|
||||
static void png_filter_paeth(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len)
|
||||
{
|
||||
int i;
|
||||
unsigned char a;
|
||||
unsigned char b;
|
||||
unsigned char c;
|
||||
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if(prev_line && i >= stride)
|
||||
{
|
||||
a = out[i - stride];
|
||||
b = prev_line[i];
|
||||
c = prev_line[i - stride];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(prev_line)
|
||||
b = prev_line[i];
|
||||
else
|
||||
b = 0;
|
||||
|
||||
if(i >= stride)
|
||||
a = out[i - stride];
|
||||
else
|
||||
a = 0;
|
||||
|
||||
c = 0;
|
||||
}
|
||||
|
||||
out[i] = in[i] + png_paeth(a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
static int png_filter(png_t* png, unsigned char* data)
|
||||
{
|
||||
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
static int png_unfilter(png_t* png, unsigned char* data)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned pos = 0;
|
||||
unsigned outpos = 0;
|
||||
unsigned char *filtered = png->png_data;
|
||||
|
||||
int stride = png->bpp;
|
||||
|
||||
while(pos < png->png_datalen)
|
||||
{
|
||||
unsigned char filter = filtered[pos];
|
||||
|
||||
pos++;
|
||||
|
||||
if(png->depth == 16)
|
||||
{
|
||||
for(i = 0; i < png->width * stride; i+=2)
|
||||
{
|
||||
*(short*)(filtered+pos+i) = (filtered[pos+i] << 8) | filtered[pos+i+1];
|
||||
}
|
||||
}
|
||||
|
||||
switch(filter)
|
||||
{
|
||||
case 0: /* none */
|
||||
memcpy(data+outpos, filtered+pos, png->width * stride);
|
||||
break;
|
||||
case 1: /* sub */
|
||||
png_filter_sub(stride, filtered+pos, data+outpos, png->width * stride);
|
||||
break;
|
||||
case 2: /* up */
|
||||
if(outpos)
|
||||
png_filter_up(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
|
||||
else
|
||||
png_filter_up(stride, filtered+pos, data+outpos, 0, png->width*stride);
|
||||
break;
|
||||
case 3: /* average */
|
||||
if(outpos)
|
||||
png_filter_average(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
|
||||
else
|
||||
png_filter_average(stride, filtered+pos, data+outpos, 0, png->width*stride);
|
||||
break;
|
||||
case 4: /* paeth */
|
||||
if(outpos)
|
||||
png_filter_paeth(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride);
|
||||
else
|
||||
png_filter_paeth(stride, filtered+pos, data+outpos, 0, png->width*stride);
|
||||
break;
|
||||
default:
|
||||
return PNG_UNKNOWN_FILTER;
|
||||
}
|
||||
|
||||
outpos += png->width * stride;
|
||||
pos += png->width * stride;
|
||||
}
|
||||
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
int png_get_data(png_t* png, unsigned char* data)
|
||||
{
|
||||
int result = PNG_NO_ERROR;
|
||||
|
||||
while(result == PNG_NO_ERROR)
|
||||
{
|
||||
result = png_process_chunk(png);
|
||||
}
|
||||
|
||||
if(result != PNG_DONE)
|
||||
{
|
||||
png_free(png->png_data);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = png_unfilter(png, data);
|
||||
|
||||
png_free(png->png_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data)
|
||||
{
|
||||
int i;
|
||||
unsigned char *filtered;
|
||||
png->width = width;
|
||||
png->height = height;
|
||||
png->depth = depth;
|
||||
png->color_type = color;
|
||||
png->bpp = png_get_bpp(png);
|
||||
|
||||
filtered = png_alloc(width * height * png->bpp + height);
|
||||
|
||||
for(i = 0; i < png->height; i++)
|
||||
{
|
||||
filtered[i*png->width*png->bpp+i] = 0;
|
||||
memcpy(&filtered[i*png->width*png->bpp+i+1], data + i * png->width*png->bpp, png->width*png->bpp);
|
||||
}
|
||||
|
||||
png_filter(png, filtered);
|
||||
png_write_ihdr(png);
|
||||
png_write_idats(png, filtered);
|
||||
|
||||
png_free(filtered);
|
||||
return PNG_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
char* png_error_string(int error)
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
case PNG_NO_ERROR:
|
||||
return "No error";
|
||||
case PNG_FILE_ERROR:
|
||||
return "Unknown file error.";
|
||||
case PNG_HEADER_ERROR:
|
||||
return "No PNG header found. Are you sure this is a PNG?";
|
||||
case PNG_IO_ERROR:
|
||||
return "Failure while reading file.";
|
||||
case PNG_EOF_ERROR:
|
||||
return "Reached end of file.";
|
||||
case PNG_CRC_ERROR:
|
||||
return "CRC or chunk length error.";
|
||||
case PNG_MEMORY_ERROR:
|
||||
return "Could not allocate memory.";
|
||||
case PNG_ZLIB_ERROR:
|
||||
return "zlib reported an error.";
|
||||
case PNG_UNKNOWN_FILTER:
|
||||
return "Unknown filter method used in scanline.";
|
||||
case PNG_DONE:
|
||||
return "PNG done";
|
||||
case PNG_NOT_SUPPORTED:
|
||||
return "The PNG is unsupported by pnglite, too bad for you!";
|
||||
case PNG_WRONG_ARGUMENTS:
|
||||
return "Wrong combination of arguments passed to png_open. You must use either a read_function or supply a file pointer to use.";
|
||||
default:
|
||||
return "Unknown error.";
|
||||
};
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/* pnglite.h - Interface for pnglite library
|
||||
Copyright (c) 2007 Daniel Karling
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
Daniel Karling
|
||||
daniel.karling@gmail.com
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _PNGLITE_H_
|
||||
#define _PNGLITE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
/*
|
||||
Enumerations for pnglite.
|
||||
Negative numbers are error codes and 0 and up are okay responses.
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
PNG_DONE = 1,
|
||||
PNG_NO_ERROR = 0,
|
||||
PNG_FILE_ERROR = -1,
|
||||
PNG_HEADER_ERROR = -2,
|
||||
PNG_IO_ERROR = -3,
|
||||
PNG_EOF_ERROR = -4,
|
||||
PNG_CRC_ERROR = -5,
|
||||
PNG_MEMORY_ERROR = -6,
|
||||
PNG_ZLIB_ERROR = -7,
|
||||
PNG_UNKNOWN_FILTER = -8,
|
||||
PNG_NOT_SUPPORTED = -9,
|
||||
PNG_WRONG_ARGUMENTS = -10
|
||||
};
|
||||
|
||||
/*
|
||||
The five different kinds of color storage in PNG files.
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
PNG_GREYSCALE = 0,
|
||||
PNG_TRUECOLOR = 2,
|
||||
PNG_INDEXED = 3,
|
||||
PNG_GREYSCALE_ALPHA = 4,
|
||||
PNG_TRUECOLOR_ALPHA = 6
|
||||
};
|
||||
|
||||
/*
|
||||
Typedefs for callbacks.
|
||||
*/
|
||||
|
||||
typedef unsigned (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);
|
||||
typedef unsigned (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer);
|
||||
typedef void (*png_free_t)(void* p);
|
||||
typedef void * (*png_alloc_t)(unsigned long s);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* zs; /* pointer to z_stream */
|
||||
png_read_callback_t read_fun;
|
||||
png_write_callback_t write_fun;
|
||||
void* user_pointer;
|
||||
|
||||
unsigned char* png_data;
|
||||
unsigned png_datalen;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned char depth;
|
||||
unsigned char color_type;
|
||||
unsigned char compression_method;
|
||||
unsigned char filter_method;
|
||||
unsigned char interlace_method;
|
||||
unsigned char bpp;
|
||||
}png_t;
|
||||
|
||||
/*
|
||||
Function: png_init
|
||||
|
||||
This function initializes pnglite. The parameters can be used to set your own memory allocation routines following these formats:
|
||||
|
||||
> void* (*custom_alloc)(unsigned long s)
|
||||
> void (*custom_free)(void* p)
|
||||
Parameters:
|
||||
pngalloc - Pointer to custom allocation routine. If 0 is passed, malloc from libc will be used.
|
||||
pngfree - Pointer to custom free routine. If 0 is passed, free from libc will be used.
|
||||
|
||||
Returns:
|
||||
Always returns PNG_NO_ERROR.
|
||||
*/
|
||||
|
||||
int png_init(png_alloc_t pngalloc, png_free_t pngfree);
|
||||
|
||||
/*
|
||||
Function: png_open_file
|
||||
|
||||
This function is used to open a png file with the internal file IO system. This function should be used instead of
|
||||
png_open if no custom read function is used.
|
||||
|
||||
Parameters:
|
||||
png - Empty png_t struct.
|
||||
filename - Filename of the file to be opened.
|
||||
|
||||
Returns:
|
||||
PNG_NO_ERROR on success, otherwise an error code.
|
||||
*/
|
||||
|
||||
int png_open_file(png_t *png, const char* filename);
|
||||
|
||||
int png_open_file_read(png_t *png, const char* filename);
|
||||
int png_open_file_write(png_t *png, const char* filename);
|
||||
|
||||
/*
|
||||
Function: png_open
|
||||
|
||||
This function reads or writes a png from/to the specified callback. The callbacks should be of the format:
|
||||
|
||||
> unsigned long (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);
|
||||
> unsigned long (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer).
|
||||
|
||||
Only one callback has to be specified. The read callback in case of PNG reading, otherwise the write callback.
|
||||
|
||||
Writing:
|
||||
The callback will be called like fwrite.
|
||||
|
||||
Reading:
|
||||
The callback will be called each time pnglite needs more data. The callback should read as much data as requested,
|
||||
or return 0. This should always be possible if the PNG is sane. If the output-buffer is a null-pointer the callback
|
||||
should only skip ahead the specified number of elements. If the callback is a null-pointer the user_pointer will be
|
||||
treated as a file pointer (use png_open_file instead).
|
||||
|
||||
Parameters:
|
||||
png - png_t struct
|
||||
read_fun - Callback function for reading.
|
||||
user_pointer - User pointer to be passed to read_fun.
|
||||
|
||||
Returns:
|
||||
PNG_NO_ERROR on success, otherwise an error code.
|
||||
*/
|
||||
|
||||
int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer);
|
||||
|
||||
int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer);
|
||||
int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer);
|
||||
|
||||
/*
|
||||
Function: png_print_info
|
||||
|
||||
This function prints some info about the opened png file to stdout.
|
||||
|
||||
Parameters:
|
||||
png - png struct to get info from.
|
||||
*/
|
||||
|
||||
void png_print_info(png_t* png);
|
||||
|
||||
/*
|
||||
Function: png_error_string
|
||||
|
||||
This function translates an error code to a human readable string.
|
||||
|
||||
Parameters:
|
||||
error - Error code.
|
||||
|
||||
Returns:
|
||||
Pointer to string.
|
||||
*/
|
||||
|
||||
char* png_error_string(int error);
|
||||
|
||||
/*
|
||||
Function: png_get_data
|
||||
|
||||
This function decodes the opened png file and stores the result in data. data should be big enough to hold the decoded png. Required size will be:
|
||||
|
||||
> width*height*(bytes per pixel)
|
||||
|
||||
Parameters:
|
||||
data - Where to store result.
|
||||
|
||||
Returns:
|
||||
PNG_NO_ERROR on success, otherwise an error code.
|
||||
*/
|
||||
|
||||
int png_get_data(png_t* png, unsigned char* data);
|
||||
|
||||
int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data);
|
||||
|
||||
/*
|
||||
Function: png_close_file
|
||||
|
||||
Closes an open png file pointer. Should only be used when the png has been opened with png_open_file.
|
||||
|
||||
Parameters:
|
||||
png - png to close.
|
||||
|
||||
Returns:
|
||||
PNG_NO_ERROR
|
||||
*/
|
||||
|
||||
int png_close_file(png_t* png);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,461 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* This is an assembly optimized version of the following WavPack function:
|
||||
*
|
||||
* void decorr_stereo_pass_cont (struct decorr_pass *dpp,
|
||||
* long *buffer, long sample_count);
|
||||
*
|
||||
* It performs a single pass of stereo decorrelation on the provided buffer.
|
||||
* Note that this version of the function requires that the 8 previous stereo
|
||||
* samples are visible and correct. In other words, it ignores the "samples_*"
|
||||
* fields in the decorr_pass structure and gets the history data directly
|
||||
* from the buffer. It does, however, return the appropriate history samples
|
||||
* to the decorr_pass structure before returning.
|
||||
*
|
||||
* This is written to work on a ARM7TDMI processor. This version only uses the
|
||||
* 32-bit multiply-accumulate instruction and so will overflow with 24-bit
|
||||
* WavPack files.
|
||||
*/
|
||||
.text
|
||||
.align
|
||||
.global decorr_stereo_pass_cont_arm
|
||||
|
||||
/*
|
||||
* on entry:
|
||||
*
|
||||
* r0 = struct decorr_pass *dpp
|
||||
* r1 = long *buffer
|
||||
* r2 = long sample_count
|
||||
*/
|
||||
|
||||
decorr_stereo_pass_cont_arm:
|
||||
|
||||
stmfd sp!, {r4 - r8, r10, r11, lr}
|
||||
mov r5, r0 @ r5 = dpp
|
||||
mov r11, #512 @ r11 = 512 for rounding
|
||||
ldrsh r6, [r0, #2] @ r6 = dpp->delta
|
||||
ldrsh r4, [r0, #4] @ r4 = dpp->weight_A
|
||||
ldrsh r0, [r0, #6] @ r0 = dpp->weight_B
|
||||
cmp r2, #0 @ exit if no samples to process
|
||||
beq common_exit
|
||||
|
||||
add r7, r1, r2, asl #3 @ r7 = buffer ending position
|
||||
ldrsh r2, [r5, #0] @ r2 = dpp->term
|
||||
cmp r2, #0
|
||||
bmi minus_term
|
||||
|
||||
ldr lr, [r1, #-16] @ load 2 sample history from buffer
|
||||
ldr r10, [r1, #-12] @ for terms 2, 17, and 18
|
||||
ldr r8, [r1, #-8]
|
||||
ldr r3, [r1, #-4]
|
||||
cmp r2, #17
|
||||
beq term_17_loop
|
||||
cmp r2, #18
|
||||
beq term_18_loop
|
||||
cmp r2, #2
|
||||
beq term_2_loop
|
||||
b term_default_loop @ else handle default (1-8, except 2)
|
||||
|
||||
minus_term:
|
||||
mov r10, #1024 @ r10 = -1024 for weight clipping
|
||||
rsb r10, r10, #0 @ (only used for negative terms)
|
||||
cmn r2, #1
|
||||
beq term_minus_1
|
||||
cmn r2, #2
|
||||
beq term_minus_2
|
||||
cmn r2, #3
|
||||
beq term_minus_3
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 17 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample
|
||||
* r3 = previous right sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = current decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_17_loop:
|
||||
rsbs ip, lr, r8, asl #1 @ decorr value = (2 * prev) - 2nd prev
|
||||
mov lr, r8 @ previous becomes 2nd previous
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mla r8, ip, r4, r11 @ mult decorr value by weight, round,
|
||||
add r8, r2, r8, asr #10 @ shift, and add to new sample
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L325
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L325: rsbs ip, r10, r3, asl #1 @ do same thing for right channel
|
||||
mov r10, r3
|
||||
ldr r2, [r1], #4
|
||||
mla r3, ip, r0, r11
|
||||
add r3, r2, r3, asr #10
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L329
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L329: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_17_loop
|
||||
b store_1718 @ common exit for terms 17 & 18
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 18 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample
|
||||
* r3 = previous right sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_18_loop:
|
||||
sub ip, r8, lr @ decorr value =
|
||||
mov lr, r8 @ ((3 * prev) - 2nd prev) >> 1
|
||||
adds ip, r8, ip, asr #1
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mla r8, ip, r4, r11 @ mult decorr value by weight, round,
|
||||
add r8, r2, r8, asr #10 @ shift, and add to new sample
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L337
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L337: sub ip, r3, r10 @ do same thing for right channel
|
||||
mov r10, r3
|
||||
adds ip, r3, ip, asr #1
|
||||
ldr r2, [r1], #4
|
||||
mla r3, ip, r0, r11
|
||||
add r3, r2, r3, asr #10
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L341
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L341: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_18_loop
|
||||
|
||||
/* common exit for terms 17 & 18 */
|
||||
|
||||
store_1718:
|
||||
str r3, [r5, #40] @ store sample history into struct
|
||||
str r8, [r5, #8]
|
||||
str r10, [r5, #44]
|
||||
str lr, [r5, #12]
|
||||
b common_exit @ and return
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 2 condition
|
||||
* (note that this case can be handled by the default term handler (1-8), but
|
||||
* this special case is faster because it doesn't have to read memory twice)
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample
|
||||
* r3 = previous right sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_2_loop:
|
||||
movs ip, lr @ get decorrelation value & test
|
||||
mov lr, r8 @ previous becomes 2nd previous
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mla r8, ip, r4, r11 @ mult decorr value by weight, round,
|
||||
add r8, r2, r8, asr #10 @ shift, and add to new sample
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L225
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L225: movs ip, r10 @ do same thing for right channel
|
||||
mov r10, r3
|
||||
ldr r2, [r1], #4
|
||||
mla r3, ip, r0, r11
|
||||
add r3, r2, r3, asr #10
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L229
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L229: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_2_loop
|
||||
b default_term_exit @ this exit updates all dpp->samples
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle default term condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = result accumulator
|
||||
* r1 = bptr r9 =
|
||||
* r2 = dpp->term r10 =
|
||||
* r3 = decorrelation value r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr =
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_default_loop:
|
||||
ldr ip, [r1] @ get original sample
|
||||
ldr r3, [r1, -r2, asl #3] @ get decorrelation value based on term
|
||||
mla r8, r3, r4, r11 @ mult decorr value by weight, round,
|
||||
add r8, ip, r8, asr #10 @ shift and add to new sample
|
||||
str r8, [r1], #4 @ store update sample
|
||||
cmp r3, #0
|
||||
cmpne ip, #0
|
||||
beq .L350
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L350: ldr ip, [r1] @ do the same thing for right channel
|
||||
ldr r3, [r1, -r2, asl #3]
|
||||
mla r8, r3, r0, r11
|
||||
add r8, ip, r8, asr #10
|
||||
str r8, [r1], #4
|
||||
cmp r3, #0
|
||||
cmpne ip, #0
|
||||
beq .L354
|
||||
teq ip, r3
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L354: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_default_loop
|
||||
|
||||
/*
|
||||
* This exit is used by terms 1-8 to store the previous 8 samples into the decorr
|
||||
* structure (even if they are not all used for the given term)
|
||||
*/
|
||||
|
||||
default_term_exit:
|
||||
ldrsh r3, [r5, #0]
|
||||
sub ip, r3, #1
|
||||
mov lr, #7
|
||||
|
||||
.L358: and r3, ip, #7
|
||||
add r3, r5, r3, asl #2
|
||||
ldr r2, [r1, #-4]
|
||||
str r2, [r3, #40]
|
||||
ldr r2, [r1, #-8]!
|
||||
str r2, [r3, #8]
|
||||
sub ip, ip, #1
|
||||
sub lr, lr, #1
|
||||
cmn lr, #1
|
||||
bne .L358
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -1 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 =
|
||||
* r1 = bptr r9 =
|
||||
* r2 = intermediate result r10 = -1024 (for clipping)
|
||||
* r3 = previous right sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = updated left sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_1:
|
||||
ldr r3, [r1, #-4]
|
||||
|
||||
term_minus_1_loop:
|
||||
ldr ip, [r1] @ for left channel the decorrelation value
|
||||
mla r2, r3, r4, r11 @ is the previous right sample (in r3)
|
||||
add lr, ip, r2, asr #10
|
||||
str lr, [r1], #8
|
||||
cmp r3, #0
|
||||
cmpne ip, #0
|
||||
beq .L361
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #1024
|
||||
movgt r4, #1024
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L361: ldr r2, [r1, #-4] @ for right channel the decorrelation value
|
||||
mla r3, lr, r0, r11 @ is the just updated right sample (in lr)
|
||||
add r3, r2, r3, asr #10
|
||||
str r3, [r1, #-4]
|
||||
cmp lr, #0
|
||||
cmpne r2, #0
|
||||
beq .L369
|
||||
teq r2, lr
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #1024 @ then clip weight to +/-1024
|
||||
movgt r0, #1024
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L369: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_1_loop
|
||||
|
||||
str r3, [r5, #8] @ else store right sample and exit
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -2 condition
|
||||
* (note that the channels are processed in the reverse order here)
|
||||
*
|
||||
* r0 = dpp->weight_B r8 =
|
||||
* r1 = bptr r9 =
|
||||
* r2 = intermediate result r10 = -1024 (for clipping)
|
||||
* r3 = previous left sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = updated right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_2:
|
||||
ldr r3, [r1, #-8]
|
||||
|
||||
term_minus_2_loop:
|
||||
ldr ip, [r1, #4] @ for right channel the decorrelation value
|
||||
mla r2, r3, r0, r11 @ is the previous left sample (in r3)
|
||||
add lr, ip, r2, asr #10
|
||||
str lr, [r1, #4]
|
||||
cmp r3, #0
|
||||
cmpne ip, #0
|
||||
beq .L380
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #1024 @ then clip weight to +/-1024
|
||||
movgt r0, #1024
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L380: ldr r2, [r1, #0] @ for left channel the decorrelation value
|
||||
mla r3, lr, r4, r11 @ is the just updated left sample (in lr)
|
||||
add r3, r2, r3, asr #10
|
||||
str r3, [r1], #8
|
||||
cmp lr, #0
|
||||
cmpne r2, #0
|
||||
beq .L388
|
||||
teq r2, lr
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #1024
|
||||
movgt r4, #1024
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L388: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_2_loop
|
||||
|
||||
str r3, [r5, #40] @ else store left channel and exit
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -3 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current left sample r10 = -1024 (for clipping)
|
||||
* r3 = previous right sample r11 = 512 (for rounding)
|
||||
* r4 = dpp->weight_A ip = intermediate result
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr =
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_3:
|
||||
ldr r3, [r1, #-4] @ load previous samples
|
||||
ldr r8, [r1, #-8]
|
||||
|
||||
term_minus_3_loop:
|
||||
ldr ip, [r1]
|
||||
mla r2, r3, r4, r11
|
||||
add r2, ip, r2, asr #10
|
||||
str r2, [r1], #4
|
||||
cmp r3, #0
|
||||
cmpne ip, #0
|
||||
beq .L399
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #1024 @ then clip weight to +/-1024
|
||||
movgt r4, #1024
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L399: movs ip, r8 @ ip = previous left we use now
|
||||
mov r8, r2 @ r8 = current left we use next time
|
||||
ldr r2, [r1], #4
|
||||
mla r3, ip, r0, r11
|
||||
add r3, r2, r3, asr #10
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L407
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #1024
|
||||
movgt r0, #1024
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L407: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_3_loop
|
||||
|
||||
str r3, [r5, #8] @ else store previous samples & exit
|
||||
str r8, [r5, #40]
|
||||
|
||||
/*
|
||||
* Before finally exiting we must store weights back for next time
|
||||
*/
|
||||
|
||||
common_exit:
|
||||
strh r4, [r5, #4]
|
||||
strh r0, [r5, #6]
|
||||
ldmfd sp!, {r4 - r8, r10, r11, pc}
|
||||
|
||||
@@ -1,491 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* This is an assembly optimized version of the following WavPack function:
|
||||
*
|
||||
* void decorr_stereo_pass_cont (struct decorr_pass *dpp,
|
||||
* long *buffer, long sample_count);
|
||||
*
|
||||
* It performs a single pass of stereo decorrelation on the provided buffer.
|
||||
* Note that this version of the function requires that the 8 previous stereo
|
||||
* samples are visible and correct. In other words, it ignores the "samples_*"
|
||||
* fields in the decorr_pass structure and gets the history data directly
|
||||
* from the buffer. It does, however, return the appropriate history samples
|
||||
* to the decorr_pass structure before returning.
|
||||
*
|
||||
* This is written to work on a ARM7TDMI processor. This version uses the
|
||||
* 64-bit multiply-accumulate instruction and so can be used with all
|
||||
* WavPack files. However, for optimum performance with 16-bit WavPack
|
||||
* files, there is a faster version that only uses the 32-bit MLA
|
||||
* instruction.
|
||||
*/
|
||||
|
||||
.text
|
||||
.align
|
||||
.global decorr_stereo_pass_cont_arml
|
||||
|
||||
/*
|
||||
* on entry:
|
||||
*
|
||||
* r0 = struct decorr_pass *dpp
|
||||
* r1 = long *buffer
|
||||
* r2 = long sample_count
|
||||
*/
|
||||
|
||||
decorr_stereo_pass_cont_arml:
|
||||
|
||||
stmfd sp!, {r4 - r8, r10, r11, lr}
|
||||
mov r5, r0 @ r5 = dpp
|
||||
mov r11, #512 @ r11 = 512 for rounding
|
||||
ldrsh r6, [r0, #2] @ r6 = dpp->delta
|
||||
ldrsh r4, [r0, #4] @ r4 = dpp->weight_A
|
||||
ldrsh r0, [r0, #6] @ r0 = dpp->weight_B
|
||||
cmp r2, #0 @ exit if no samples to process
|
||||
beq common_exit
|
||||
|
||||
mov r0, r0, asl #18 @ for 64-bit math we use weights << 18
|
||||
mov r4, r4, asl #18
|
||||
mov r6, r6, asl #18
|
||||
add r7, r1, r2, asl #3 @ r7 = buffer ending position
|
||||
ldrsh r2, [r5, #0] @ r2 = dpp->term
|
||||
cmp r2, #0
|
||||
blt minus_term
|
||||
|
||||
ldr lr, [r1, #-16] @ load 2 sample history from buffer
|
||||
ldr r10, [r1, #-12] @ for terms 2, 17, and 18
|
||||
ldr r8, [r1, #-8]
|
||||
ldr r3, [r1, #-4]
|
||||
|
||||
cmp r2, #18
|
||||
beq term_18_loop
|
||||
mov lr, lr, asl #4
|
||||
mov r10, r10, asl #4
|
||||
cmp r2, #2
|
||||
beq term_2_loop
|
||||
cmp r2, #17
|
||||
beq term_17_loop
|
||||
b term_default_loop
|
||||
|
||||
minus_term:
|
||||
mov r10, #(1024 << 18) @ r10 = -1024 << 18 for weight clipping
|
||||
rsb r10, r10, #0 @ (only used for negative terms)
|
||||
cmn r2, #1
|
||||
beq term_minus_1
|
||||
cmn r2, #2
|
||||
beq term_minus_2
|
||||
cmn r2, #3
|
||||
beq term_minus_3
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 17 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample << 4
|
||||
* r3 = previous right sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = current decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample << 4
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_17_loop:
|
||||
rsbs ip, lr, r8, asl #5 @ decorr value = (2 * prev) - 2nd prev
|
||||
mov lr, r8, asl #4 @ previous becomes 2nd previous
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mov r11, #0x80000000
|
||||
mov r8, r2
|
||||
smlalne r11, r8, r4, ip
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L325
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L325: rsbs ip, r10, r3, asl #5 @ do same thing for right channel
|
||||
mov r10, r3, asl #4
|
||||
ldr r2, [r1], #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r0, ip
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L329
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L329: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_17_loop
|
||||
mov lr, lr, asr #4
|
||||
mov r10, r10, asr #4
|
||||
b store_1718 @ common exit for terms 17 & 18
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 18 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample
|
||||
* r3 = previous right sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_18_loop:
|
||||
rsb ip, lr, r8 @ decorr value =
|
||||
mov lr, r8 @ ((3 * prev) - 2nd prev) >> 1
|
||||
add ip, lr, ip, asr #1
|
||||
movs ip, ip, asl #4
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mov r11, #0x80000000
|
||||
mov r8, r2
|
||||
smlalne r11, r8, r4, ip
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L337
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L337: rsb ip, r10, r3 @ do same thing for right channel
|
||||
mov r10, r3
|
||||
add ip, r10, ip, asr #1
|
||||
movs ip, ip, asl #4
|
||||
ldr r2, [r1], #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r0, ip
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L341
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L341: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_18_loop
|
||||
|
||||
/* common exit for terms 17 & 18 */
|
||||
|
||||
store_1718:
|
||||
str r3, [r5, #40] @ store sample history into struct
|
||||
str r8, [r5, #8]
|
||||
str r10, [r5, #44]
|
||||
str lr, [r5, #12]
|
||||
b common_exit @ and return
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = 2 condition
|
||||
* (note that this case can be handled by the default term handler (1-8), but
|
||||
* this special case is faster because it doesn't have to read memory twice)
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current sample r10 = second previous left sample << 4
|
||||
* r3 = previous right sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = decorrelation value
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = second previous right sample << 4
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_2_loop:
|
||||
movs ip, lr @ get decorrelation value & test
|
||||
ldr r2, [r1], #4 @ get sample & update pointer
|
||||
mov lr, r8, asl #4 @ previous becomes 2nd previous
|
||||
mov r11, #0x80000000
|
||||
mov r8, r2
|
||||
smlalne r11, r8, r4, ip
|
||||
strne r8, [r1, #-4] @ if change possible, store sample back
|
||||
cmpne r2, #0
|
||||
beq .L225
|
||||
teq ip, r2 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L225: movs ip, r10 @ do same thing for right channel
|
||||
ldr r2, [r1], #4
|
||||
mov r10, r3, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r0, ip
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L229
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L229: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_2_loop
|
||||
|
||||
b default_term_exit @ this exit updates all dpp->samples
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle default term condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = result accumulator
|
||||
* r1 = bptr r9 =
|
||||
* r2 = dpp->term r10 =
|
||||
* r3 = decorrelation value r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr =
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_default_loop:
|
||||
ldr r3, [r1, -r2, asl #3] @ get decorrelation value based on term
|
||||
ldr ip, [r1], #4 @ get original sample and bump ptr
|
||||
movs r3, r3, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r8, ip
|
||||
smlalne r11, r8, r4, r3
|
||||
strne r8, [r1, #-4] @ if possibly changed, store updated sample
|
||||
cmpne ip, #0
|
||||
beq .L350
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
|
||||
.L350: ldr r3, [r1, -r2, asl #3] @ do the same thing for right channel
|
||||
ldr ip, [r1], #4
|
||||
movs r3, r3, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r8, ip
|
||||
smlalne r11, r8, r0, r3
|
||||
strne r8, [r1, #-4]
|
||||
cmpne ip, #0
|
||||
beq .L354
|
||||
teq ip, r3
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
|
||||
.L354: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_default_loop
|
||||
|
||||
/*
|
||||
* This exit is used by terms 1-8 to store the previous 8 samples into the decorr
|
||||
* structure (even if they are not all used for the given term)
|
||||
*/
|
||||
|
||||
default_term_exit:
|
||||
ldrsh r3, [r5, #0]
|
||||
sub ip, r3, #1
|
||||
mov lr, #7
|
||||
|
||||
.L358: and r3, ip, #7
|
||||
add r3, r5, r3, asl #2
|
||||
ldr r2, [r1, #-4]
|
||||
str r2, [r3, #40]
|
||||
ldr r2, [r1, #-8]!
|
||||
str r2, [r3, #8]
|
||||
sub ip, ip, #1
|
||||
sub lr, lr, #1
|
||||
cmn lr, #1
|
||||
bne .L358
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -1 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 =
|
||||
* r1 = bptr r9 =
|
||||
* r2 = intermediate result r10 = -1024 (for clipping)
|
||||
* r3 = previous right sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = updated left sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_1:
|
||||
ldr r3, [r1, #-4]
|
||||
|
||||
term_minus_1_loop:
|
||||
ldr ip, [r1], #8 @ for left channel the decorrelation value
|
||||
movs r3, r3, asl #4 @ is the previous right sample (in r3)
|
||||
mov r11, #0x80000000
|
||||
mov lr, ip
|
||||
smlalne r11, lr, r4, r3
|
||||
strne lr, [r1, #-8]
|
||||
cmpne ip, #0
|
||||
beq .L361
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #(1024 << 18)
|
||||
movgt r4, #(1024 << 18)
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L361: ldr r2, [r1, #-4] @ for right channel the decorrelation value
|
||||
movs lr, lr, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r0, lr
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L369
|
||||
teq r2, lr
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #(1024 << 18) @ then clip weight to +/-1024
|
||||
movgt r0, #(1024 << 18)
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L369: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_1_loop
|
||||
|
||||
str r3, [r5, #8] @ else store right sample and exit
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -2 condition
|
||||
* (note that the channels are processed in the reverse order here)
|
||||
*
|
||||
* r0 = dpp->weight_B r8 =
|
||||
* r1 = bptr r9 =
|
||||
* r2 = intermediate result r10 = -1024 (for clipping)
|
||||
* r3 = previous left sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = current sample
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr = updated right sample
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_2:
|
||||
ldr r3, [r1, #-8]
|
||||
|
||||
term_minus_2_loop:
|
||||
ldr ip, [r1, #4] @ for right channel the decorrelation value
|
||||
movs r3, r3, asl #4 @ is the previous left sample (in r3)
|
||||
mov r11, #0x80000000
|
||||
mov lr, ip
|
||||
smlalne r11, lr, r0, r3
|
||||
strne lr, [r1, #4]
|
||||
cmpne ip, #0
|
||||
beq .L380
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #(1024 << 18) @ then clip weight to +/-1024
|
||||
movgt r0, #(1024 << 18)
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L380: ldr r2, [r1], #8 @ for left channel the decorrelation value
|
||||
movs lr, lr, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r4, lr
|
||||
strne r3, [r1, #-8]
|
||||
cmpne r2, #0
|
||||
beq .L388
|
||||
teq r2, lr
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #(1024 << 18)
|
||||
movgt r4, #(1024 << 18)
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L388: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_2_loop
|
||||
|
||||
str r3, [r5, #40] @ else store left channel and exit
|
||||
b common_exit
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
* Loop to handle term = -3 condition
|
||||
*
|
||||
* r0 = dpp->weight_B r8 = previous left sample
|
||||
* r1 = bptr r9 =
|
||||
* r2 = current left sample r10 = -1024 (for clipping)
|
||||
* r3 = previous right sample r11 = lo accumulator (for rounding)
|
||||
* r4 = dpp->weight_A ip = intermediate result
|
||||
* r5 = dpp sp =
|
||||
* r6 = dpp->delta lr =
|
||||
* r7 = eptr pc =
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
term_minus_3:
|
||||
ldr r3, [r1, #-4] @ load previous samples
|
||||
ldr r8, [r1, #-8]
|
||||
|
||||
term_minus_3_loop:
|
||||
ldr ip, [r1], #4
|
||||
movs r3, r3, asl #4
|
||||
mov r11, #0x80000000
|
||||
mov r2, ip
|
||||
smlalne r11, r2, r4, r3
|
||||
strne r2, [r1, #-4]
|
||||
cmpne ip, #0
|
||||
beq .L399
|
||||
teq ip, r3 @ update weight based on signs
|
||||
submi r4, r4, r6
|
||||
addpl r4, r4, r6
|
||||
cmp r4, #(1024 << 18) @ then clip weight to +/-1024
|
||||
movgt r4, #(1024 << 18)
|
||||
cmp r4, r10
|
||||
movlt r4, r10
|
||||
|
||||
.L399: movs ip, r8, asl #4 @ ip = previous left we use now
|
||||
mov r8, r2 @ r8 = current left we use next time
|
||||
ldr r2, [r1], #4
|
||||
mov r11, #0x80000000
|
||||
mov r3, r2
|
||||
smlalne r11, r3, r0, ip
|
||||
strne r3, [r1, #-4]
|
||||
cmpne r2, #0
|
||||
beq .L407
|
||||
teq ip, r2
|
||||
submi r0, r0, r6
|
||||
addpl r0, r0, r6
|
||||
cmp r0, #(1024 << 18)
|
||||
movgt r0, #(1024 << 18)
|
||||
cmp r0, r10
|
||||
movlt r0, r10
|
||||
|
||||
.L407: cmp r7, r1 @ loop back if more samples to do
|
||||
bhi term_minus_3_loop
|
||||
|
||||
str r3, [r5, #8] @ else store previous samples & exit
|
||||
str r8, [r5, #40]
|
||||
|
||||
/*
|
||||
* Before finally exiting we must store weights back for next time
|
||||
*/
|
||||
|
||||
common_exit:
|
||||
mov r0, r0, asr #18 @ restore weights to real magnitude
|
||||
mov r4, r4, asr #18
|
||||
strh r4, [r5, #4]
|
||||
strh r0, [r5, #6]
|
||||
ldmfd sp!, {r4 - r8, r10, r11, pc}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// bits.c
|
||||
|
||||
// This module provides utilities to support the BitStream structure which is
|
||||
// used to read and write all WavPack audio data streams. It also contains a
|
||||
// wrapper for the stream I/O functions and a set of functions dealing with
|
||||
// endian-ness, both for enhancing portability. Finally, a debug wrapper for
|
||||
// the malloc() system is provided.
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
////////////////////////// Bitstream functions ////////////////////////////////
|
||||
|
||||
// Open the specified BitStream and associate with the specified buffer.
|
||||
|
||||
static void bs_read (Bitstream *bs);
|
||||
|
||||
void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end, read_stream file, uint32_t file_bytes)
|
||||
{
|
||||
CLEAR (*bs);
|
||||
bs->buf = buffer_start;
|
||||
bs->end = buffer_end;
|
||||
|
||||
if (file) {
|
||||
bs->ptr = bs->end - 1;
|
||||
bs->file_bytes = file_bytes;
|
||||
bs->file = file;
|
||||
}
|
||||
else
|
||||
bs->ptr = bs->buf - 1;
|
||||
|
||||
bs->wrap = bs_read;
|
||||
}
|
||||
|
||||
// This function is only called from the getbit() and getbits() macros when
|
||||
// the BitStream has been exhausted and more data is required. Sinve these
|
||||
// bistreams no longer access files, this function simple sets an error and
|
||||
// resets the buffer.
|
||||
|
||||
static void bs_read (Bitstream *bs)
|
||||
{
|
||||
if (bs->file && bs->file_bytes) {
|
||||
uint32_t bytes_read, bytes_to_read = bs->end - bs->buf;
|
||||
|
||||
if (bytes_to_read > bs->file_bytes)
|
||||
bytes_to_read = bs->file_bytes;
|
||||
|
||||
bytes_read = bs->file (bs->buf, bytes_to_read);
|
||||
|
||||
if (bytes_read) {
|
||||
bs->end = bs->buf + bytes_read;
|
||||
bs->file_bytes -= bytes_read;
|
||||
}
|
||||
else {
|
||||
memset (bs->buf, -1, bs->end - bs->buf);
|
||||
bs->error = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
bs->error = 1;
|
||||
|
||||
if (bs->error)
|
||||
memset (bs->buf, -1, bs->end - bs->buf);
|
||||
|
||||
bs->ptr = bs->buf;
|
||||
}
|
||||
|
||||
/////////////////////// Endian Correction Routines ////////////////////////////
|
||||
|
||||
void little_endian_to_native (void *data, char *format)
|
||||
{
|
||||
uchar *cp = (uchar *) data;
|
||||
int32_t temp;
|
||||
|
||||
while (*format) {
|
||||
switch (*format) {
|
||||
case 'L':
|
||||
temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24);
|
||||
* (int32_t *) cp = temp;
|
||||
cp += 4;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
temp = cp [0] + (cp [1] << 8);
|
||||
* (short *) cp = (short) temp;
|
||||
cp += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isdigit (*format))
|
||||
cp += *format - '0';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
void native_to_little_endian (void *data, char *format)
|
||||
{
|
||||
uchar *cp = (uchar *) data;
|
||||
int32_t temp;
|
||||
|
||||
while (*format) {
|
||||
switch (*format) {
|
||||
case 'L':
|
||||
temp = * (int32_t *) cp;
|
||||
*cp++ = (uchar) temp;
|
||||
*cp++ = (uchar) (temp >> 8);
|
||||
*cp++ = (uchar) (temp >> 16);
|
||||
*cp++ = (uchar) (temp >> 24);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
temp = * (short *) cp;
|
||||
*cp++ = (uchar) temp;
|
||||
*cp++ = (uchar) (temp >> 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isdigit (*format))
|
||||
cp += *format - '0';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
format++;
|
||||
}
|
||||
}
|
||||
@@ -1,525 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* This is an assembly optimized version of the following WavPack function:
|
||||
*
|
||||
* void decorr_stereo_pass_cont (struct decorr_pass *dpp,
|
||||
* long *buffer, long sample_count);
|
||||
*
|
||||
* It performs a single pass of stereo decorrelation on the provided buffer.
|
||||
* Note that this version of the function requires that the 8 previous stereo
|
||||
* samples are visible and correct. In other words, it ignores the "samples_*"
|
||||
* fields in the decorr_pass structure and gets the history data directly
|
||||
* from the buffer. It does, however, return the appropriate history samples
|
||||
* to the decorr_pass structure before returning.
|
||||
*
|
||||
* This is written to work on a MCF5249 processor, or any processor based on
|
||||
* the ColdFire V2 core with an EMAC unit. The EMAC is perfectly suited for
|
||||
* the "apply_weight" function of WavPack decorrelation because it provides
|
||||
* the requires 40-bit product. The fractional rounding mode of the EMAC is not
|
||||
* configurable and uses "round to even" while WavPack uses "round to larger",
|
||||
* so the rounding has to be done manually.
|
||||
*/
|
||||
|
||||
.text
|
||||
.align 2
|
||||
.global decorr_stereo_pass_cont_mcf5249
|
||||
|
||||
decorr_stereo_pass_cont_mcf5249:
|
||||
|
||||
lea (-44, %sp), %sp
|
||||
movem.l %d2-%d7/%a2-%a6, (%sp)
|
||||
move.l 44+4(%sp), %a2 | a2 = dpp->
|
||||
move.l 44+8(%sp), %a1 | a1 = bptr
|
||||
move.w 2(%a2), %a3 | a3 = dpp->delta
|
||||
move.w 4(%a2), %d3 | d3 = dpp->weight_A (sign extended)
|
||||
ext.l %d3
|
||||
move.w 6(%a2), %d4 | d4 = dpp->weight_B (sign extended)
|
||||
ext.l %d4
|
||||
move.l 44+12(%sp), %d0 | d0 = sample_count
|
||||
jbeq return_only | if zero, nothing to do
|
||||
|
||||
lsl.l #3, %d0 | d5 = bptr + (sample_count * 8)
|
||||
move.l %d0, %d5
|
||||
add.l %a1, %d5
|
||||
|
||||
moveq.l #17, %d0 | left shift weights & delta 17 places
|
||||
asl.l %d0, %d3
|
||||
asl.l %d0, %d4
|
||||
move.l %a3, %d1
|
||||
asl.l %d0, %d1
|
||||
move.l %d1, %a3
|
||||
|
||||
moveq.l #0x20, %d6
|
||||
move.l %d6, %macsr | set fractional mode for MAC
|
||||
move.l #0, %acc1 | acc1 = 0x00 0000 80 (for rounding)
|
||||
move.l #0x800000, %accext01
|
||||
|
||||
move.l #1024<<17, %d6 | d6 & d7 are weight clipping limits
|
||||
move.l #-1024<<17, %d7 | (only used by negative terms)
|
||||
|
||||
move.w (%a2), %d0 | d0 = term
|
||||
ext.l %d0
|
||||
cmp.l #17, %d0
|
||||
jbeq term_17 | term = 17
|
||||
cmp.l #18, %d0
|
||||
jbeq term_18 | term = 18
|
||||
addq.l #1, %d0
|
||||
jbeq term_minus_1 | term = -1
|
||||
addq.l #1, %d0
|
||||
jbeq term_minus_2 | term = -2
|
||||
addq.l #1, %d0
|
||||
jbeq term_minus_3 | term = -3
|
||||
jbra term_default | default term = 1 - 8
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle term = 17 condition
|
||||
|
|
||||
| a0 = d0 = (2 * bptr [-1]) - bptr [-2]
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_17:
|
||||
move.l -8(%a1), %d0 | d0 = 2 * bptr [-1] - bptr [-2]
|
||||
add.l %d0, %d0
|
||||
sub.l -16(%a1), %d0
|
||||
beq .L251 | if zero, skip calculation
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_A
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L255
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L256 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
sub.l %a3, %d3 | subtract again instead of branch
|
||||
.L256: add.l %a3, %d3 | add delta to weight
|
||||
|
||||
.L255: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | update bptr [0] and store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L253: move.l -8(%a1), %d0 | d0 = 2 * bptr [-1] - bptr [-2]
|
||||
add.l %d0, %d0
|
||||
sub.l -16(%a1), %d0
|
||||
beq .L257 | if zero, skip calculations
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_B
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L254
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L259 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
sub.l %a3, %d4 | subtract again instead of branch
|
||||
.L259: add.l %a3, %d4 | add delta to weight
|
||||
|
||||
.L254: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | update bptr [0] and store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L252: cmp.l %a1, %d5 | loop if bptr < eptr
|
||||
jbhi term_17
|
||||
bra term_17_18_finish | exit through common path
|
||||
|
||||
.L251: addq.l #4, %a1 | update point and jump back into loop
|
||||
bra .L253
|
||||
|
||||
.L257: addq.l #4, %a1 | update point and jump back into loop
|
||||
bra .L252
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle term = 18 condition
|
||||
|
|
||||
| a0 = d0 = ((3 * bptr [-1]) - bptr [-2]) >> 1
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_18:
|
||||
move.l -8(%a1), %a0 | d0 = (3 * bptr [-1] - bptr [-2]) >> 1
|
||||
lea (%a0,%a0.l*2), %a0
|
||||
move.l %a0, %d0
|
||||
sub.l -16(%a1), %d0
|
||||
asr.l #1, %d0
|
||||
beq .L260
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_A
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L266
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L267 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
sub.l %a3, %d3 | subtract again instead of branch
|
||||
.L267: add.l %a3, %d3 | add delta to weight
|
||||
|
||||
.L266: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [0], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L268: move.l -8(%a1), %a0 | d0 = (3 * bptr [-1] - bptr [-2]) >> 1
|
||||
lea (%a0,%a0.l*2), %a0
|
||||
move.l %a0, %d0
|
||||
sub.l -16(%a1), %d0
|
||||
asr.l #1, %d0
|
||||
beq .L261
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_B
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L265
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L270 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
sub.l %a3, %d4 | subtract again instead of branch
|
||||
.L270: add.l %a3, %d4 | add delta to weight
|
||||
|
||||
.L265: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [0], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L269: cmp.l %a1, %d5 | loop if bptr < eptr
|
||||
jbhi term_18
|
||||
bra term_17_18_finish | exit through common path
|
||||
|
||||
.L260: addq.l #4, %a1 | bump pointer and jump back into loop
|
||||
bra .L268
|
||||
|
||||
.L261: addq.l #4, %a1 | bump pointer and jump back into loop
|
||||
bra .L269
|
||||
|
||||
term_17_18_finish:
|
||||
move.l -4(%a1), 40(%a2) | restore dpp->samples_A [0-1], B [0-1]
|
||||
move.l -8(%a1), 8(%a2)
|
||||
move.l -12(%a1), 44(%a2)
|
||||
move.l -16(%a1), 12(%a2)
|
||||
jbra finish_up
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle default terms (i.e. 1 - 8)
|
||||
|
|
||||
| a0 = tptr d0 = tptr [0]
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_default:
|
||||
move.w (%a2), %d0 | a0 = a1 - (dpp->term * 8)
|
||||
ext.l %d0
|
||||
lsl.l #3, %d0
|
||||
move.l %a1, %a0
|
||||
sub.l %d0, %a0
|
||||
|
||||
term_default_loop:
|
||||
move.l (%a0)+, %d0 | d0 = tptr [0], skip ahead if zero
|
||||
beq .L271
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_A
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L277
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L278 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
sub.l %a3, %d3 | subtract again instead of branch
|
||||
.L278: add.l %a3, %d3 | add delta to weight
|
||||
|
||||
.L277: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [0], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L275: move.l (%a0)+, %d0 | d0 = tptr [0], skip ahead if zero
|
||||
beq .L272
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + (d0 << 4) * weight_B
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L276
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L281 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
sub.l %a3, %d4 | subtract again instead of branch
|
||||
.L281: add.l %a3, %d4 | add delta to weight
|
||||
|
||||
.L276: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [0], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L274: cmp.l %a1, %d5 | loop back if bptr < eptr
|
||||
jbhi term_default_loop
|
||||
move.w (%a2), %d0 | d0 = term - 1
|
||||
moveq.l #8, %d1 | d1 = loop counter
|
||||
|
||||
.L323: subq.l #1, %d0 | back up & mask index
|
||||
and.l #7, %d0
|
||||
move.l -(%a1), 40(%a2,%d0.l*4) | store dpp->samples_B [d0]
|
||||
move.l -(%a1), 8(%a2,%d0.l*4) | store dpp->samples_A [d0]
|
||||
subq.l #1, %d1 | loop on count
|
||||
jbne .L323
|
||||
jbra finish_up
|
||||
|
||||
.L271: addq.l #4, %a1 | bump pointer and jump back into loop
|
||||
bra .L275
|
||||
|
||||
.L272: addq.l #4, %a1 | bump pointer and jump back into loop
|
||||
bra .L274
|
||||
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle term = -1 condition
|
||||
|
|
||||
| a0 = d0 = decorrelation sample
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| a6 = d6 = 1024 << 17
|
||||
| a7 = d7 = -1024 << 17
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_minus_1:
|
||||
move.l -4(%a1), %d0 | d0 = bptr [-1]
|
||||
beq .L402
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_A)
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L405
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L404 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
cmp.l %d7, %d3 | check for negative clip limit
|
||||
bge .L405
|
||||
move.l %d7, %d3
|
||||
bra .L405
|
||||
|
||||
.L404: add.l %a3, %d3 | add delta to weight
|
||||
cmp.l %d6, %d3 | check for positive clip limit
|
||||
ble .L405
|
||||
move.l %d6, %d3
|
||||
|
||||
.L405: move.l %acc0, %d0 | d2 = rounded product
|
||||
add.l %d1, %d0 | add applied weight to bptr [0], store
|
||||
move.l %d0, (%a1)+
|
||||
beq .L401
|
||||
|
||||
.L410: move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_B)
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L403
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L407 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
cmp.l %d7, %d4 | check for negative clip limit
|
||||
bge .L403
|
||||
move.l %d7, %d4
|
||||
bra .L403
|
||||
|
||||
.L407: add.l %a3, %d4 | add delta to weight
|
||||
cmp.l %d6, %d4 | check for positive clip limit
|
||||
ble .L403
|
||||
move.l %d6, %d4
|
||||
|
||||
.L403: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [1], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L411: cmp.l %a1, %d5 | loop back if bptr < eptr
|
||||
jbhi term_minus_1
|
||||
move.l -4(%a1), 8(%a2) | dpp->samples_A [0] = bptr [-1]
|
||||
jbra finish_up
|
||||
|
||||
.L402: move.l (%a1)+, %d0
|
||||
bne .L410
|
||||
|
||||
.L401: addq.l #4, %a1
|
||||
bra .L411
|
||||
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle term = -2 condition
|
||||
|
|
||||
| a0 = d0 = decorrelation sample
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| a6 = d6 = 1024 << 17
|
||||
| a7 = d7 = -1024 << 17
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_minus_2:
|
||||
move.l -8(%a1), %d0 | d0 = bptr [-2]
|
||||
beq .L511
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_B)
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l 4(%a1), %d1
|
||||
beq .L505
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L504 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
cmp.l %d7, %d4 | ckeck for negative clip limit
|
||||
bge .L505
|
||||
move.l %d7, %d4
|
||||
bra .L505
|
||||
|
||||
.L504: add.l %a3, %d4 | add delta to weight
|
||||
cmp.l %d6, %d4 | check for positive clip limit
|
||||
ble .L505
|
||||
move.l %d6, %d4
|
||||
|
||||
.L505: move.l %acc0, %d0 | d2 = rounded product
|
||||
add.l %d1, %d0 | add applied weight to bptr [0], store
|
||||
move.l %d0, 4(%a1)
|
||||
beq .L512
|
||||
|
||||
.L510: move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_A)
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L503
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L507 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
cmp.l %d7, %d3 | check for negative clip limit
|
||||
bge .L503
|
||||
move.l %d7, %d3
|
||||
bra .L503
|
||||
|
||||
.L507: add.l %a3, %d3 | add delta to weight
|
||||
cmp.l %d6, %d3 | check for negative clip limit
|
||||
ble .L503
|
||||
move.l %d6, %d3
|
||||
|
||||
.L503: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [1], store
|
||||
move.l %d2, (%a1)
|
||||
|
||||
.L512: addq.l #8, %a1
|
||||
cmp.l %a1, %d5 | loop if bptr < eptr
|
||||
jbhi term_minus_2
|
||||
move.l -8(%a1), 40(%a2) | dpp->samples_B [0] = bptr [-4]
|
||||
jbra finish_up
|
||||
|
||||
.L511: move.l 4(%a1), %d0
|
||||
beq .L512
|
||||
bra .L510
|
||||
|
||||
|
||||
|------------------------------------------------------------------------------
|
||||
| Loop to handle term = -3 condition
|
||||
|
|
||||
| a0 = d0 = decorrelation sample
|
||||
| a1 = bptr d1 = initial bptr [0]
|
||||
| a2 = dpp-> d2 = updated bptr [0]
|
||||
| a3 = dpp->delta << 17 d3 = dpp->weight_A << 17
|
||||
| a4 = d4 = dpp->weight_B << 17
|
||||
| a5 = d5 = eptr
|
||||
| a6 = d6 = 1024 << 17
|
||||
| a7 = d7 = -1024 << 17
|
||||
| macsr = 0x20 acc1 = 0x00 0000 80
|
||||
|------------------------------------------------------------------------------
|
||||
|
||||
term_minus_3:
|
||||
move.l -4(%a1), %d0 | d0 = bptr [-1]
|
||||
beq .L301
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_A)
|
||||
mac.l %d0, %d3, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L320
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L319 | if same, add delta to weight
|
||||
sub.l %a3, %d3 | else subtract delta from weight
|
||||
cmp.l %d7, %d3 | check for negative clip limit
|
||||
bge .L320
|
||||
move.l %d7, %d3
|
||||
bra .L320
|
||||
|
||||
.L319: add.l %a3, %d3 | add delta to weight
|
||||
cmp.l %d6, %d3 | check for positive clip limit
|
||||
ble .L320
|
||||
move.l %d6, %d3
|
||||
|
||||
.L320: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [0], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L330: move.l -12(%a1), %d0 | d0 = bptr [-2]
|
||||
beq .L302
|
||||
move.l %acc1, %acc0
|
||||
asl.l #4, %d0 | acc0 = acc1 + ((d0 << 4) * weight_B)
|
||||
mac.l %d0, %d4, %acc0
|
||||
move.l (%a1), %d1
|
||||
beq .L318
|
||||
eor.l %d1, %d0 | else compare signs
|
||||
bge .L322 | if same, add delta to weight
|
||||
sub.l %a3, %d4 | else subtract delta from weight
|
||||
cmp.l %d7, %d4 | check for negative clip limit
|
||||
bge .L318
|
||||
move.l %d7, %d4
|
||||
bra .L318
|
||||
|
||||
.L322: add.l %a3, %d4 | add delta to weight
|
||||
cmp.l %d6, %d4 | check for positive clip limit
|
||||
ble .L318
|
||||
move.l %d6, %d4
|
||||
|
||||
.L318: move.l %acc0, %d2 | d2 = rounded product
|
||||
add.l %d1, %d2 | add applied weight to bptr [1], store
|
||||
move.l %d2, (%a1)+
|
||||
|
||||
.L331: cmp.l %a1, %d5 | bptr, eptr
|
||||
jbhi term_minus_3
|
||||
move.l -4(%a1), 8(%a2) | dpp->samples_A [0] = bptr [-1]
|
||||
move.l -8(%a1), 40(%a2) | dpp->samples_B [0] = bptr [-2]
|
||||
jbra finish_up
|
||||
|
||||
.L301: addq.l #4, %a1
|
||||
bra .L330
|
||||
|
||||
.L302: addq.l #4, %a1
|
||||
bra .L331
|
||||
|
||||
| finish and return
|
||||
|
||||
finish_up:
|
||||
moveq.l #17, %d0
|
||||
asr.l %d0, %d3
|
||||
asr.l %d0, %d4
|
||||
move.w %d3, 4(%a2) | weight_A, dpp->weight_A
|
||||
move.w %d4, 6(%a2) | weight_B, dpp->weight_B
|
||||
|
||||
clr.l %d0 | clear up EMAC
|
||||
move.l %d0, %acc0
|
||||
move.l %d0, %acc1
|
||||
|
||||
return_only:
|
||||
movem.l (%sp), %d2-%d7/%a2-%a6
|
||||
lea (44,%sp), %sp
|
||||
rts
|
||||
@@ -1,50 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// float.c
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
int bytecnt = wpmd->byte_length;
|
||||
char *byteptr = wpmd->data;
|
||||
|
||||
if (bytecnt != 4)
|
||||
return FALSE;
|
||||
|
||||
wps->float_flags = *byteptr++;
|
||||
wps->float_shift = *byteptr++;
|
||||
wps->float_max_exp = *byteptr++;
|
||||
wps->float_norm_exp = *byteptr;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void float_values (WavpackStream *wps, int32_t *values, int32_t num_values)
|
||||
{
|
||||
int shift = wps->float_max_exp - wps->float_norm_exp + wps->float_shift;
|
||||
|
||||
if (shift > 32)
|
||||
shift = 32;
|
||||
else if (shift < -32)
|
||||
shift = -32;
|
||||
|
||||
while (num_values--) {
|
||||
if (shift > 0)
|
||||
*values <<= shift;
|
||||
else if (shift < 0)
|
||||
*values >>= -shift;
|
||||
|
||||
if (*values > 8388607L)
|
||||
*values = 8388607L;
|
||||
else if (*values < -8388608L)
|
||||
*values = -8388608L;
|
||||
|
||||
values++;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
Copyright (c) 1998 - 2006 Conifer Software
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Conifer Software nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
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 REGENTS 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.
|
||||
@@ -1,105 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// metadata.c
|
||||
|
||||
// This module handles the metadata structure introduced in WavPack 4.0
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd)
|
||||
{
|
||||
uchar tchar;
|
||||
|
||||
if (!wpc->infile (&wpmd->id, 1) || !wpc->infile (&tchar, 1))
|
||||
return FALSE;
|
||||
|
||||
wpmd->byte_length = tchar << 1;
|
||||
|
||||
if (wpmd->id & ID_LARGE) {
|
||||
wpmd->id &= ~ID_LARGE;
|
||||
|
||||
if (!wpc->infile (&tchar, 1))
|
||||
return FALSE;
|
||||
|
||||
wpmd->byte_length += (int32_t) tchar << 9;
|
||||
|
||||
if (!wpc->infile (&tchar, 1))
|
||||
return FALSE;
|
||||
|
||||
wpmd->byte_length += (int32_t) tchar << 17;
|
||||
}
|
||||
|
||||
if (wpmd->id & ID_ODD_SIZE) {
|
||||
wpmd->id &= ~ID_ODD_SIZE;
|
||||
wpmd->byte_length--;
|
||||
}
|
||||
|
||||
if (wpmd->byte_length && wpmd->byte_length <= sizeof (wpc->read_buffer)) {
|
||||
uint32_t bytes_to_read = wpmd->byte_length + (wpmd->byte_length & 1);
|
||||
|
||||
if (wpc->infile (wpc->read_buffer, bytes_to_read) != (int32_t) bytes_to_read) {
|
||||
wpmd->data = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wpmd->data = wpc->read_buffer;
|
||||
}
|
||||
else
|
||||
wpmd->data = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
|
||||
switch (wpmd->id) {
|
||||
case ID_DUMMY:
|
||||
return TRUE;
|
||||
|
||||
case ID_DECORR_TERMS:
|
||||
return read_decorr_terms (wps, wpmd);
|
||||
|
||||
case ID_DECORR_WEIGHTS:
|
||||
return read_decorr_weights (wps, wpmd);
|
||||
|
||||
case ID_DECORR_SAMPLES:
|
||||
return read_decorr_samples (wps, wpmd);
|
||||
|
||||
case ID_ENTROPY_VARS:
|
||||
return read_entropy_vars (wps, wpmd);
|
||||
|
||||
case ID_HYBRID_PROFILE:
|
||||
return read_hybrid_profile (wps, wpmd);
|
||||
|
||||
case ID_FLOAT_INFO:
|
||||
return read_float_info (wps, wpmd);
|
||||
|
||||
case ID_INT32_INFO:
|
||||
return read_int32_info (wps, wpmd);
|
||||
|
||||
case ID_CHANNEL_INFO:
|
||||
return read_channel_info (wpc, wpmd);
|
||||
|
||||
case ID_CONFIG_BLOCK:
|
||||
return read_config_info (wpc, wpmd);
|
||||
|
||||
case ID_WV_BITSTREAM:
|
||||
return init_wv_bitstream (wpc, wpmd);
|
||||
|
||||
case ID_SHAPING_WEIGHTS:
|
||||
case ID_WVC_BITSTREAM:
|
||||
case ID_WVX_BITSTREAM:
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
This package contains a tiny version of the WavPack 4.40 decoder that might
|
||||
be used in a "resource limited" CPU environment or form the basis for a
|
||||
hardware decoding implementation. It is packaged with a demo command-line
|
||||
program that accepts a WavPack audio file on stdin and outputs a RIFF wav
|
||||
file to stdout. The program is standard C, and a win32 executable is
|
||||
included which was compiled under MS Visual C++ 6.0 using this command:
|
||||
|
||||
cl /O1 /DWIN32 wvfilter.c wputils.c unpack.c float.c metadata.c words.c bits.c
|
||||
|
||||
WavPack data is read with a stream reading callback. No direct seeking is
|
||||
provided for, but it is possible to start decoding anywhere in a WavPack
|
||||
stream. In this case, WavPack will be able to provide the sample-accurate
|
||||
position when it synchs with the data and begins decoding. The WIN32 macro
|
||||
is used for Windows to force the stdin and stdout streams to be binary mode.
|
||||
|
||||
Compared to the previous version, this library has been optimized somewhat
|
||||
for improved performance in exchange for slightly larger code size. The
|
||||
library also now includes hand-optimized assembly language versions of the
|
||||
decorrelation functions for both the ColdFire (w/EMAC) and ARM processors.
|
||||
|
||||
For demonstration purposes this uses a single static copy of the
|
||||
WavpackContext structure, so obviously it cannot be used for more than one
|
||||
file at a time. Also, this decoder will not handle "correction" files, plays
|
||||
only the first two channels of multi-channel files, and is limited in
|
||||
resolution in some large integer or floating point files (but always
|
||||
provides at least 24 bits of resolution). It also will not accept WavPack
|
||||
files from before version 4.0.
|
||||
|
||||
The previous version of this library would handle float files by returning
|
||||
32-bit floating-point data (even though no floating point math was used).
|
||||
Because this library would normally be used for simply playing WavPack
|
||||
files where lossless performance (beyond 24-bits) is not relevant, I have
|
||||
changed this behavior. Now, these files will generate clipped 24-bit data.
|
||||
The MODE_FLOAT flag will still be returned by WavpackGetMode(), but the
|
||||
BitsPerSample and BytesPerSample queries will be 24 and 3, respectfully.
|
||||
What this means is that an application that can handle 24-bit data will
|
||||
now be able to handle floating point data (assuming that the MODE_FLOAT
|
||||
flag is ignored).
|
||||
|
||||
To make this code viable on the greatest number of hardware platforms, the
|
||||
following are true:
|
||||
|
||||
speed is about 5x realtime on an AMD K6 300 MHz
|
||||
("high" mode 16/44 stereo; normal mode is about twice that fast)
|
||||
|
||||
no floating-point math required; just 32b * 32b = 32b int multiply
|
||||
|
||||
large data areas are static and less than 4K total
|
||||
executable code and tables are less than 40K
|
||||
no malloc / free usage
|
||||
|
||||
To maintain compatibility on various platforms, the following conventions
|
||||
are used:
|
||||
|
||||
a "char" must be exactly 8-bits
|
||||
a "short" must be exactly 16-bits
|
||||
an "int" must be at least 16-bits, but may be larger
|
||||
the "long" type is not used to avoid problems with 64-bit compilers
|
||||
|
||||
Questions or comments should be directed to david@wavpack.com
|
||||
@@ -1,785 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// unpack.c
|
||||
|
||||
// This module actually handles the decompression of the audio data, except
|
||||
// for the entropy decoding which is handled by the words.c module. For
|
||||
// maximum efficiency, the conversion is isolated to tight loops that handle
|
||||
// an entire buffer.
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LOSSY_MUTE
|
||||
|
||||
///////////////////////////// executable code ////////////////////////////////
|
||||
|
||||
// This function initializes everything required to unpack a WavPack block
|
||||
// and must be called before unpack_samples() is called to obtain audio data.
|
||||
// It is assumed that the WavpackHeader has been read into the wps->wphdr
|
||||
// (in the current WavpackStream). This is where all the metadata blocks are
|
||||
// scanned up to the one containing the audio bitstream.
|
||||
|
||||
int unpack_init (WavpackContext *wpc)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
WavpackMetadata wpmd;
|
||||
|
||||
if (wps->wphdr.block_samples && wps->wphdr.block_index != (uint32_t) -1)
|
||||
wps->sample_index = wps->wphdr.block_index;
|
||||
|
||||
wps->mute_error = FALSE;
|
||||
wps->crc = 0xffffffff;
|
||||
CLEAR (wps->wvbits);
|
||||
CLEAR (wps->decorr_passes);
|
||||
CLEAR (wps->w);
|
||||
|
||||
while (read_metadata_buff (wpc, &wpmd)) {
|
||||
if (!process_metadata (wpc, &wpmd)) {
|
||||
strcpy (wpc->error_message, "invalid metadata!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wpmd.id == ID_WV_BITSTREAM)
|
||||
break;
|
||||
}
|
||||
|
||||
if (wps->wphdr.block_samples && !bs_is_open (&wps->wvbits)) {
|
||||
strcpy (wpc->error_message, "invalid WavPack file!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (wps->wphdr.block_samples) {
|
||||
if ((wps->wphdr.flags & INT32_DATA) && wps->int32_sent_bits)
|
||||
wpc->lossy_blocks = TRUE;
|
||||
|
||||
if ((wps->wphdr.flags & FLOAT_DATA) &&
|
||||
wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME))
|
||||
wpc->lossy_blocks = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This function initialzes the main bitstream for audio samples, which must
|
||||
// be in the "wv" file.
|
||||
|
||||
int init_wv_bitstream (WavpackContext *wpc, WavpackMetadata *wpmd)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
|
||||
if (wpmd->data)
|
||||
bs_open_read (&wps->wvbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length, NULL, 0);
|
||||
else if (wpmd->byte_length)
|
||||
bs_open_read (&wps->wvbits, wpc->read_buffer, wpc->read_buffer + sizeof (wpc->read_buffer),
|
||||
wpc->infile, wpmd->byte_length + (wpmd->byte_length & 1));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read decorrelation terms from specified metadata block into the
|
||||
// decorr_passes array. The terms range from -3 to 8, plus 17 & 18;
|
||||
// other values are reserved and generate errors for now. The delta
|
||||
// ranges from 0 to 7 with all values valid. Note that the terms are
|
||||
// stored in the opposite order in the decorr_passes array compared
|
||||
// to packing.
|
||||
|
||||
int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
int termcnt = wpmd->byte_length;
|
||||
uchar *byteptr = wpmd->data;
|
||||
struct decorr_pass *dpp;
|
||||
|
||||
if (termcnt > MAX_NTERMS)
|
||||
return FALSE;
|
||||
|
||||
wps->num_terms = termcnt;
|
||||
|
||||
for (dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) {
|
||||
dpp->term = (int)(*byteptr & 0x1f) - 5;
|
||||
dpp->delta = (*byteptr++ >> 5) & 0x7;
|
||||
|
||||
if (!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read decorrelation weights from specified metadata block into the
|
||||
// decorr_passes array. The weights range +/-1024, but are rounded and
|
||||
// truncated to fit in signed chars for metadata storage. Weights are
|
||||
// separate for the two channels and are specified from the "last" term
|
||||
// (first during encode). Unspecified weights are set to zero.
|
||||
|
||||
int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
int termcnt = wpmd->byte_length, tcount;
|
||||
signed char *byteptr = wpmd->data;
|
||||
struct decorr_pass *dpp;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA))
|
||||
termcnt /= 2;
|
||||
|
||||
if (termcnt > wps->num_terms)
|
||||
return FALSE;
|
||||
|
||||
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
|
||||
dpp->weight_A = dpp->weight_B = 0;
|
||||
|
||||
while (--dpp >= wps->decorr_passes && termcnt--) {
|
||||
dpp->weight_A = restore_weight (*byteptr++);
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA))
|
||||
dpp->weight_B = restore_weight (*byteptr++);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read decorrelation samples from specified metadata block into the
|
||||
// decorr_passes array. The samples are signed 32-bit values, but are
|
||||
// converted to signed log2 values for storage in metadata. Values are
|
||||
// stored for both channels and are specified from the "last" term
|
||||
// (first during encode) with unspecified samples set to zero. The
|
||||
// number of samples stored varies with the actual term value, so
|
||||
// those must obviously come first in the metadata.
|
||||
|
||||
int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
uchar *byteptr = wpmd->data;
|
||||
uchar *endptr = byteptr + wpmd->byte_length;
|
||||
struct decorr_pass *dpp;
|
||||
int tcount;
|
||||
|
||||
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
|
||||
CLEAR (dpp->samples_A);
|
||||
CLEAR (dpp->samples_B);
|
||||
}
|
||||
|
||||
if (wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) {
|
||||
byteptr += 2;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA))
|
||||
byteptr += 2;
|
||||
}
|
||||
|
||||
while (dpp-- > wps->decorr_passes && byteptr < endptr)
|
||||
if (dpp->term > MAX_TERM) {
|
||||
dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
dpp->samples_A [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
|
||||
byteptr += 4;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
dpp->samples_B [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
dpp->samples_B [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
|
||||
byteptr += 4;
|
||||
}
|
||||
}
|
||||
else if (dpp->term < 0) {
|
||||
dpp->samples_A [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
dpp->samples_B [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8)));
|
||||
byteptr += 4;
|
||||
}
|
||||
else {
|
||||
int m = 0, cnt = dpp->term;
|
||||
|
||||
while (cnt--) {
|
||||
dpp->samples_A [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
byteptr += 2;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
dpp->samples_B [m] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
byteptr += 2;
|
||||
}
|
||||
|
||||
m++;
|
||||
}
|
||||
}
|
||||
|
||||
return byteptr == endptr;
|
||||
}
|
||||
|
||||
// Read the int32 data from the specified metadata into the specified stream.
|
||||
// This data is used for integer data that has more than 24 bits of magnitude
|
||||
// or, in some cases, used to eliminate redundant bits from any audio stream.
|
||||
|
||||
int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
int bytecnt = wpmd->byte_length;
|
||||
char *byteptr = wpmd->data;
|
||||
|
||||
if (bytecnt != 4)
|
||||
return FALSE;
|
||||
|
||||
wps->int32_sent_bits = *byteptr++;
|
||||
wps->int32_zeros = *byteptr++;
|
||||
wps->int32_ones = *byteptr++;
|
||||
wps->int32_dups = *byteptr;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read multichannel information from metadata. The first byte is the total
|
||||
// number of channels and the following bytes represent the channel_mask
|
||||
// as described for Microsoft WAVEFORMATEX.
|
||||
|
||||
int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd)
|
||||
{
|
||||
int bytecnt = wpmd->byte_length, shift = 0;
|
||||
char *byteptr = wpmd->data;
|
||||
uint32_t mask = 0;
|
||||
|
||||
if (!bytecnt || bytecnt > 5)
|
||||
return FALSE;
|
||||
|
||||
wpc->config.num_channels = *byteptr++;
|
||||
|
||||
while (--bytecnt) {
|
||||
mask |= (uint32_t) *byteptr++ << shift;
|
||||
shift += 8;
|
||||
}
|
||||
|
||||
wpc->config.channel_mask = mask;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read configuration information from metadata.
|
||||
|
||||
int read_config_info (WavpackContext *wpc, WavpackMetadata *wpmd)
|
||||
{
|
||||
int bytecnt = wpmd->byte_length;
|
||||
uchar *byteptr = wpmd->data;
|
||||
|
||||
if (bytecnt >= 3) {
|
||||
wpc->config.flags &= 0xff;
|
||||
wpc->config.flags |= (int32_t) *byteptr++ << 8;
|
||||
wpc->config.flags |= (int32_t) *byteptr++ << 16;
|
||||
wpc->config.flags |= (int32_t) *byteptr << 24;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This monster actually unpacks the WavPack bitstream(s) into the specified
|
||||
// buffer as 32-bit integers or floats (depending on orignal data). Lossy
|
||||
// samples will be clipped to their original limits (i.e. 8-bit samples are
|
||||
// clipped to -128/+127) but are still returned in int32_ts. It is up to the
|
||||
// caller to potentially reformat this for the final output including any
|
||||
// multichannel distribution, block alignment or endian compensation. The
|
||||
// function unpack_init() must have been called and the entire WavPack block
|
||||
// must still be visible (although wps->blockbuff will not be accessed again).
|
||||
// For maximum clarity, the function is broken up into segments that handle
|
||||
// various modes. This makes for a few extra infrequent flag checks, but
|
||||
// makes the code easier to follow because the nesting does not become so
|
||||
// deep. For maximum efficiency, the conversion is isolated to tight loops
|
||||
// that handle an entire buffer. The function returns the total number of
|
||||
// samples unpacked, which can be less than the number requested if an error
|
||||
// occurs or the end of the block is reached.
|
||||
|
||||
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||
extern void decorr_stereo_pass_cont_mcf5249 (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
#elif defined(CPU_ARM) && !defined(SIMULATOR)
|
||||
extern void decorr_stereo_pass_cont_arm (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
extern void decorr_stereo_pass_cont_arml (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
#else
|
||||
static void decorr_stereo_pass_cont (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
#endif
|
||||
|
||||
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
|
||||
static void fixup_samples (WavpackStream *wps, int32_t *buffer, uint32_t sample_count);
|
||||
|
||||
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
uint32_t flags = wps->wphdr.flags, crc = wps->crc, i;
|
||||
int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2;
|
||||
struct decorr_pass *dpp;
|
||||
int32_t *bptr, *eptr;
|
||||
int tcount;
|
||||
|
||||
if (wps->sample_index + sample_count > wps->wphdr.block_index + wps->wphdr.block_samples)
|
||||
sample_count = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index;
|
||||
|
||||
if (wps->mute_error) {
|
||||
memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8));
|
||||
wps->sample_index += sample_count;
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
if (flags & HYBRID_FLAG)
|
||||
mute_limit *= 2;
|
||||
|
||||
///////////////////// handle version 4 mono data /////////////////////////
|
||||
|
||||
if (flags & MONO_DATA) {
|
||||
eptr = buffer + sample_count;
|
||||
i = get_words (buffer, sample_count, flags, &wps->w, &wps->wvbits);
|
||||
|
||||
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
|
||||
decorr_mono_pass (dpp, buffer, sample_count);
|
||||
|
||||
for (bptr = buffer; bptr < eptr; ++bptr) {
|
||||
if (labs (bptr [0]) > mute_limit) {
|
||||
i = bptr - buffer;
|
||||
break;
|
||||
}
|
||||
|
||||
crc = crc * 3 + bptr [0];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////// handle version 4 stereo data ////////////////////////
|
||||
|
||||
else {
|
||||
eptr = buffer + (sample_count * 2);
|
||||
i = get_words (buffer, sample_count, flags, &wps->w, &wps->wvbits);
|
||||
|
||||
if (sample_count < 16)
|
||||
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
|
||||
decorr_stereo_pass (dpp, buffer, sample_count);
|
||||
else
|
||||
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
|
||||
decorr_stereo_pass (dpp, buffer, 8);
|
||||
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||
decorr_stereo_pass_cont_mcf5249 (dpp, buffer + 16, sample_count - 8);
|
||||
#elif defined(CPU_ARM) && !defined(SIMULATOR)
|
||||
if (((flags & MAG_MASK) >> MAG_LSB) > 15)
|
||||
decorr_stereo_pass_cont_arml (dpp, buffer + 16, sample_count - 8);
|
||||
else
|
||||
decorr_stereo_pass_cont_arm (dpp, buffer + 16, sample_count - 8);
|
||||
#else
|
||||
decorr_stereo_pass_cont (dpp, buffer + 16, sample_count - 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (flags & JOINT_STEREO)
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
bptr [0] += (bptr [1] -= (bptr [0] >> 1));
|
||||
|
||||
if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) {
|
||||
i = (bptr - buffer) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
crc = (crc * 3 + bptr [0]) * 3 + bptr [1];
|
||||
}
|
||||
else
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) {
|
||||
i = (bptr - buffer) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
crc = (crc * 3 + bptr [0]) * 3 + bptr [1];
|
||||
}
|
||||
}
|
||||
|
||||
if (i != sample_count) {
|
||||
memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8));
|
||||
wps->mute_error = TRUE;
|
||||
i = sample_count;
|
||||
}
|
||||
|
||||
fixup_samples (wps, buffer, i);
|
||||
|
||||
if (flags & FALSE_STEREO) {
|
||||
int32_t *dptr = buffer + i * 2;
|
||||
int32_t *sptr = buffer + i;
|
||||
int32_t c = i;
|
||||
|
||||
while (c--) {
|
||||
*--dptr = *--sptr;
|
||||
*--dptr = *sptr;
|
||||
}
|
||||
}
|
||||
|
||||
wps->sample_index += i;
|
||||
wps->crc = crc;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count)
|
||||
{
|
||||
int32_t delta = dpp->delta, weight_A = dpp->weight_A, weight_B = dpp->weight_B;
|
||||
int32_t *bptr, *eptr = buffer + (sample_count * 2), sam_A, sam_B;
|
||||
int m, k;
|
||||
|
||||
switch (dpp->term) {
|
||||
|
||||
case 17:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
|
||||
dpp->samples_A [1] = dpp->samples_A [0];
|
||||
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [0];
|
||||
|
||||
sam_A = 2 * dpp->samples_B [0] - dpp->samples_B [1];
|
||||
dpp->samples_B [1] = dpp->samples_B [0];
|
||||
dpp->samples_B [0] = apply_weight (weight_B, sam_A) + bptr [1];
|
||||
update_weight (weight_B, delta, sam_A, bptr [1]);
|
||||
bptr [1] = dpp->samples_B [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 18:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
|
||||
dpp->samples_A [1] = dpp->samples_A [0];
|
||||
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [0];
|
||||
|
||||
sam_A = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
|
||||
dpp->samples_B [1] = dpp->samples_B [0];
|
||||
dpp->samples_B [0] = apply_weight (weight_B, sam_A) + bptr [1];
|
||||
update_weight (weight_B, delta, sam_A, bptr [1]);
|
||||
bptr [1] = dpp->samples_B [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = dpp->samples_A [m];
|
||||
dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [k];
|
||||
|
||||
sam_A = dpp->samples_B [m];
|
||||
dpp->samples_B [k] = apply_weight (weight_B, sam_A) + bptr [1];
|
||||
update_weight (weight_B, delta, sam_A, bptr [1]);
|
||||
bptr [1] = dpp->samples_B [k];
|
||||
|
||||
m = (m + 1) & (MAX_TERM - 1);
|
||||
k = (k + 1) & (MAX_TERM - 1);
|
||||
}
|
||||
|
||||
if (m) {
|
||||
int32_t temp_samples [MAX_TERM];
|
||||
|
||||
memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A));
|
||||
|
||||
for (k = 0; k < MAX_TERM; k++, m++)
|
||||
dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)];
|
||||
|
||||
memcpy (temp_samples, dpp->samples_B, sizeof (dpp->samples_B));
|
||||
|
||||
for (k = 0; k < MAX_TERM; k++, m++)
|
||||
dpp->samples_B [k] = temp_samples [m & (MAX_TERM - 1)];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case -1:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = bptr [0] + apply_weight (weight_A, dpp->samples_A [0]);
|
||||
update_weight_clip (weight_A, delta, dpp->samples_A [0], bptr [0]);
|
||||
bptr [0] = sam_A;
|
||||
dpp->samples_A [0] = bptr [1] + apply_weight (weight_B, sam_A);
|
||||
update_weight_clip (weight_B, delta, sam_A, bptr [1]);
|
||||
bptr [1] = dpp->samples_A [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case -2:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_B = bptr [1] + apply_weight (weight_B, dpp->samples_B [0]);
|
||||
update_weight_clip (weight_B, delta, dpp->samples_B [0], bptr [1]);
|
||||
bptr [1] = sam_B;
|
||||
dpp->samples_B [0] = bptr [0] + apply_weight (weight_A, sam_B);
|
||||
update_weight_clip (weight_A, delta, sam_B, bptr [0]);
|
||||
bptr [0] = dpp->samples_B [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case -3:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = bptr [0] + apply_weight (weight_A, dpp->samples_A [0]);
|
||||
update_weight_clip (weight_A, delta, dpp->samples_A [0], bptr [0]);
|
||||
sam_B = bptr [1] + apply_weight (weight_B, dpp->samples_B [0]);
|
||||
update_weight_clip (weight_B, delta, dpp->samples_B [0], bptr [1]);
|
||||
bptr [0] = dpp->samples_B [0] = sam_A;
|
||||
bptr [1] = dpp->samples_A [0] = sam_B;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
dpp->weight_A = weight_A;
|
||||
dpp->weight_B = weight_B;
|
||||
}
|
||||
|
||||
#if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM)) || defined(SIMULATOR)
|
||||
|
||||
static void decorr_stereo_pass_cont (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count)
|
||||
{
|
||||
int32_t delta = dpp->delta, weight_A = dpp->weight_A, weight_B = dpp->weight_B;
|
||||
int32_t *bptr, *tptr, *eptr = buffer + (sample_count * 2), sam_A, sam_B;
|
||||
int k, i;
|
||||
|
||||
switch (dpp->term) {
|
||||
|
||||
case 17:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = 2 * bptr [-2] - bptr [-4];
|
||||
bptr [0] = apply_weight (weight_A, sam_A) + (sam_B = bptr [0]);
|
||||
update_weight (weight_A, delta, sam_A, sam_B);
|
||||
|
||||
sam_A = 2 * bptr [-1] - bptr [-3];
|
||||
bptr [1] = apply_weight (weight_B, sam_A) + (sam_B = bptr [1]);
|
||||
update_weight (weight_B, delta, sam_A, sam_B);
|
||||
}
|
||||
|
||||
dpp->samples_B [0] = bptr [-1];
|
||||
dpp->samples_A [0] = bptr [-2];
|
||||
dpp->samples_B [1] = bptr [-3];
|
||||
dpp->samples_A [1] = bptr [-4];
|
||||
break;
|
||||
|
||||
case 18:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
sam_A = (3 * bptr [-2] - bptr [-4]) >> 1;
|
||||
bptr [0] = apply_weight (weight_A, sam_A) + (sam_B = bptr [0]);
|
||||
update_weight (weight_A, delta, sam_A, sam_B);
|
||||
|
||||
sam_A = (3 * bptr [-1] - bptr [-3]) >> 1;
|
||||
bptr [1] = apply_weight (weight_B, sam_A) + (sam_B = bptr [1]);
|
||||
update_weight (weight_B, delta, sam_A, sam_B);
|
||||
}
|
||||
|
||||
dpp->samples_B [0] = bptr [-1];
|
||||
dpp->samples_A [0] = bptr [-2];
|
||||
dpp->samples_B [1] = bptr [-3];
|
||||
dpp->samples_A [1] = bptr [-4];
|
||||
break;
|
||||
|
||||
default:
|
||||
for (bptr = buffer, tptr = buffer - (dpp->term * 2); bptr < eptr; bptr += 2, tptr += 2) {
|
||||
bptr [0] = apply_weight (weight_A, tptr [0]) + (sam_A = bptr [0]);
|
||||
update_weight (weight_A, delta, tptr [0], sam_A);
|
||||
|
||||
bptr [1] = apply_weight (weight_B, tptr [1]) + (sam_A = bptr [1]);
|
||||
update_weight (weight_B, delta, tptr [1], sam_A);
|
||||
}
|
||||
|
||||
for (k = dpp->term - 1, i = 8; i--; k--) {
|
||||
dpp->samples_B [k & (MAX_TERM - 1)] = *--bptr;
|
||||
dpp->samples_A [k & (MAX_TERM - 1)] = *--bptr;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case -1:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
bptr [0] = apply_weight (weight_A, bptr [-1]) + (sam_A = bptr [0]);
|
||||
update_weight_clip (weight_A, delta, bptr [-1], sam_A);
|
||||
bptr [1] = apply_weight (weight_B, bptr [0]) + (sam_A = bptr [1]);
|
||||
update_weight_clip (weight_B, delta, bptr [0], sam_A);
|
||||
}
|
||||
|
||||
dpp->samples_A [0] = bptr [-1];
|
||||
break;
|
||||
|
||||
case -2:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
bptr [1] = apply_weight (weight_B, bptr [-2]) + (sam_A = bptr [1]);
|
||||
update_weight_clip (weight_B, delta, bptr [-2], sam_A);
|
||||
bptr [0] = apply_weight (weight_A, bptr [1]) + (sam_A = bptr [0]);
|
||||
update_weight_clip (weight_A, delta, bptr [1], sam_A);
|
||||
}
|
||||
|
||||
dpp->samples_B [0] = bptr [-2];
|
||||
break;
|
||||
|
||||
case -3:
|
||||
for (bptr = buffer; bptr < eptr; bptr += 2) {
|
||||
bptr [0] = apply_weight (weight_A, bptr [-1]) + (sam_A = bptr [0]);
|
||||
update_weight_clip (weight_A, delta, bptr [-1], sam_A);
|
||||
bptr [1] = apply_weight (weight_B, bptr [-2]) + (sam_A = bptr [1]);
|
||||
update_weight_clip (weight_B, delta, bptr [-2], sam_A);
|
||||
}
|
||||
|
||||
dpp->samples_A [0] = bptr [-1];
|
||||
dpp->samples_B [0] = bptr [-2];
|
||||
break;
|
||||
}
|
||||
|
||||
dpp->weight_A = weight_A;
|
||||
dpp->weight_B = weight_B;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count)
|
||||
{
|
||||
int32_t delta = dpp->delta, weight_A = dpp->weight_A;
|
||||
int32_t *bptr, *eptr = buffer + sample_count, sam_A;
|
||||
int m, k;
|
||||
|
||||
switch (dpp->term) {
|
||||
|
||||
case 17:
|
||||
for (bptr = buffer; bptr < eptr; bptr++) {
|
||||
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
|
||||
dpp->samples_A [1] = dpp->samples_A [0];
|
||||
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 18:
|
||||
for (bptr = buffer; bptr < eptr; bptr++) {
|
||||
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
|
||||
dpp->samples_A [1] = dpp->samples_A [0];
|
||||
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [0];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr++) {
|
||||
sam_A = dpp->samples_A [m];
|
||||
dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0];
|
||||
update_weight (weight_A, delta, sam_A, bptr [0]);
|
||||
bptr [0] = dpp->samples_A [k];
|
||||
m = (m + 1) & (MAX_TERM - 1);
|
||||
k = (k + 1) & (MAX_TERM - 1);
|
||||
}
|
||||
|
||||
if (m) {
|
||||
int32_t temp_samples [MAX_TERM];
|
||||
|
||||
memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A));
|
||||
|
||||
for (k = 0; k < MAX_TERM; k++, m++)
|
||||
dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
dpp->weight_A = weight_A;
|
||||
}
|
||||
|
||||
|
||||
// This is a helper function for unpack_samples() that applies several final
|
||||
// operations. First, if the data is 32-bit float data, then that conversion
|
||||
// is done in the float.c module (whether lossy or lossless) and we return.
|
||||
// Otherwise, if the extended integer data applies, then that operation is
|
||||
// executed first. If the unpacked data is lossy (and not corrected) then
|
||||
// it is clipped and shifted in a single operation. Otherwise, if it's
|
||||
// lossless then the last step is to apply the final shift (if any).
|
||||
|
||||
static void fixup_samples (WavpackStream *wps, int32_t *buffer, uint32_t sample_count)
|
||||
{
|
||||
uint32_t flags = wps->wphdr.flags;
|
||||
int shift = (flags & SHIFT_MASK) >> SHIFT_LSB;
|
||||
|
||||
if (flags & FLOAT_DATA) {
|
||||
float_values (wps, buffer, (flags & MONO_FLAG) ? sample_count : sample_count * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & INT32_DATA) {
|
||||
uint32_t count = (flags & MONO_FLAG) ? sample_count : sample_count * 2;
|
||||
int sent_bits = wps->int32_sent_bits, zeros = wps->int32_zeros;
|
||||
int ones = wps->int32_ones, dups = wps->int32_dups;
|
||||
int32_t *dptr = buffer;
|
||||
|
||||
if (!(flags & HYBRID_FLAG) && !sent_bits && (zeros + ones + dups))
|
||||
while (count--) {
|
||||
if (zeros)
|
||||
*dptr <<= zeros;
|
||||
else if (ones)
|
||||
*dptr = ((*dptr + 1) << ones) - 1;
|
||||
else if (dups)
|
||||
*dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1);
|
||||
|
||||
dptr++;
|
||||
}
|
||||
else
|
||||
shift += zeros + sent_bits + ones + dups;
|
||||
}
|
||||
|
||||
if (flags & HYBRID_FLAG) {
|
||||
int32_t min_value, max_value, min_shifted, max_shifted;
|
||||
|
||||
switch (flags & BYTES_STORED) {
|
||||
case 0:
|
||||
min_shifted = (min_value = -128 >> shift) << shift;
|
||||
max_shifted = (max_value = 127 >> shift) << shift;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
min_shifted = (min_value = -32768 >> shift) << shift;
|
||||
max_shifted = (max_value = 32767 >> shift) << shift;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
min_shifted = (min_value = -8388608 >> shift) << shift;
|
||||
max_shifted = (max_value = 8388607 >> shift) << shift;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
default:
|
||||
min_shifted = (min_value = (int32_t) 0x80000000 >> shift) << shift;
|
||||
max_shifted = (max_value = (int32_t) 0x7FFFFFFF >> shift) << shift;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(flags & MONO_FLAG))
|
||||
sample_count *= 2;
|
||||
|
||||
while (sample_count--) {
|
||||
if (*buffer < min_value)
|
||||
*buffer++ = min_shifted;
|
||||
else if (*buffer > max_value)
|
||||
*buffer++ = max_shifted;
|
||||
else
|
||||
*buffer++ <<= shift;
|
||||
}
|
||||
}
|
||||
else if (shift) {
|
||||
if (!(flags & MONO_FLAG))
|
||||
sample_count *= 2;
|
||||
|
||||
while (sample_count--)
|
||||
*buffer++ <<= shift;
|
||||
}
|
||||
}
|
||||
|
||||
// This function checks the crc value(s) for an unpacked block, returning the
|
||||
// number of actual crc errors detected for the block. The block must be
|
||||
// completely unpacked before this test is valid. For losslessly unpacked
|
||||
// blocks of float or extended integer data the extended crc is also checked.
|
||||
// Note that WavPack's crc is not a CCITT approved polynomial algorithm, but
|
||||
// is a much simpler method that is virtually as robust for real world data.
|
||||
|
||||
int check_crc_error (WavpackContext *wpc)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
int result = 0;
|
||||
|
||||
if (wps->crc != wps->wphdr.crc)
|
||||
++result;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
/*////////////////////////////////////////////////////////////////////////// */
|
||||
/* **** WAVPACK **** // */
|
||||
/* Hybrid Lossless Wavefile Compressor // */
|
||||
/* Copyright (c) 1998 - 2004 Conifer Software. // */
|
||||
/* All Rights Reserved. // */
|
||||
/* Distributed under the BSD Software License (see license.txt) // */
|
||||
/*////////////////////////////////////////////////////////////////////////// */
|
||||
|
||||
/* wavpack.h */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* This header file contains all the definitions required by WavPack. */
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
typedef unsigned long uint32_t;
|
||||
typedef long int32_t;
|
||||
#elif defined(_WIN32) && !defined(__MINGW32__)
|
||||
#include <stdlib.h>
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef __int32 int32_t;
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
#if !defined(__GNUC__) || defined(WIN32)
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
/*//////////////////////////// WavPack Header ///////////////////////////////// */
|
||||
|
||||
/* Note that this is the ONLY structure that is written to (or read from) */
|
||||
/* WavPack 4.0 files, and is the preamble to every block in both the .wv */
|
||||
/* and .wvc files. */
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
short version;
|
||||
uchar track_no, index_no;
|
||||
uint32_t total_samples, block_index, block_samples, flags, crc;
|
||||
} WavpackHeader;
|
||||
|
||||
#define WavpackHeaderFormat "4LS2LLLLL"
|
||||
|
||||
/* or-values for "flags" */
|
||||
|
||||
#define BYTES_STORED 3 /* 1-4 bytes/sample */
|
||||
#define MONO_FLAG 4 /* not stereo */
|
||||
#define HYBRID_FLAG 8 /* hybrid mode */
|
||||
#define JOINT_STEREO 0x10 /* joint stereo */
|
||||
#define CROSS_DECORR 0x20 /* no-delay cross decorrelation */
|
||||
#define HYBRID_SHAPE 0x40 /* noise shape (hybrid mode only) */
|
||||
#define FLOAT_DATA 0x80 /* ieee 32-bit floating point data */
|
||||
|
||||
#define INT32_DATA 0x100 /* special extended int handling */
|
||||
#define HYBRID_BITRATE 0x200 /* bitrate noise (hybrid mode only) */
|
||||
#define HYBRID_BALANCE 0x400 /* balance noise (hybrid stereo mode only) */
|
||||
|
||||
#define INITIAL_BLOCK 0x800 /* initial block of multichannel segment */
|
||||
#define FINAL_BLOCK 0x1000 /* final block of multichannel segment */
|
||||
|
||||
#define SHIFT_LSB 13
|
||||
#define SHIFT_MASK (0x1fL << SHIFT_LSB)
|
||||
|
||||
#define MAG_LSB 18
|
||||
#define MAG_MASK (0x1fL << MAG_LSB)
|
||||
|
||||
#define SRATE_LSB 23
|
||||
#define SRATE_MASK (0xfL << SRATE_LSB)
|
||||
|
||||
#define FALSE_STEREO 0x40000000 /* block is stereo, but data is mono */
|
||||
|
||||
#define IGNORED_FLAGS 0x18000000 /* reserved, but ignore if encountered */
|
||||
#define NEW_SHAPING 0x20000000 /* use IIR filter for negative shaping */
|
||||
#define UNKNOWN_FLAGS 0x80000000 /* also reserved, but refuse decode if */
|
||||
/* encountered */
|
||||
|
||||
#define MONO_DATA (MONO_FLAG | FALSE_STEREO)
|
||||
|
||||
#define MIN_STREAM_VERS 0x402 /* lowest stream version we'll decode */
|
||||
#define MAX_STREAM_VERS 0x410 /* highest stream version we'll decode */
|
||||
|
||||
/*////////////////////////// WavPack Metadata ///////////////////////////////// */
|
||||
|
||||
/* This is an internal representation of metadata. */
|
||||
|
||||
typedef struct {
|
||||
int32_t byte_length;
|
||||
void *data;
|
||||
uchar id;
|
||||
} WavpackMetadata;
|
||||
|
||||
#define ID_OPTIONAL_DATA 0x20
|
||||
#define ID_ODD_SIZE 0x40
|
||||
#define ID_LARGE 0x80
|
||||
|
||||
#define ID_DUMMY 0x0
|
||||
#define ID_ENCODER_INFO 0x1
|
||||
#define ID_DECORR_TERMS 0x2
|
||||
#define ID_DECORR_WEIGHTS 0x3
|
||||
#define ID_DECORR_SAMPLES 0x4
|
||||
#define ID_ENTROPY_VARS 0x5
|
||||
#define ID_HYBRID_PROFILE 0x6
|
||||
#define ID_SHAPING_WEIGHTS 0x7
|
||||
#define ID_FLOAT_INFO 0x8
|
||||
#define ID_INT32_INFO 0x9
|
||||
#define ID_WV_BITSTREAM 0xa
|
||||
#define ID_WVC_BITSTREAM 0xb
|
||||
#define ID_WVX_BITSTREAM 0xc
|
||||
#define ID_CHANNEL_INFO 0xd
|
||||
|
||||
#define ID_RIFF_HEADER (ID_OPTIONAL_DATA | 0x1)
|
||||
#define ID_RIFF_TRAILER (ID_OPTIONAL_DATA | 0x2)
|
||||
#define ID_REPLAY_GAIN (ID_OPTIONAL_DATA | 0x3)
|
||||
#define ID_CUESHEET (ID_OPTIONAL_DATA | 0x4)
|
||||
#define ID_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0x5)
|
||||
#define ID_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x6)
|
||||
|
||||
/*/////////////////////// WavPack Configuration /////////////////////////////// */
|
||||
|
||||
/* This internal structure is used during encode to provide configuration to */
|
||||
/* the encoding engine and during decoding to provide fle information back to */
|
||||
/* the higher level functions. Not all fields are used in both modes. */
|
||||
|
||||
typedef struct {
|
||||
int bits_per_sample, bytes_per_sample;
|
||||
int num_channels, float_norm_exp;
|
||||
uint32_t flags, sample_rate, channel_mask;
|
||||
} WavpackConfig;
|
||||
|
||||
#define CONFIG_BYTES_STORED 3 /* 1-4 bytes/sample */
|
||||
#define CONFIG_MONO_FLAG 4 /* not stereo */
|
||||
#define CONFIG_HYBRID_FLAG 8 /* hybrid mode */
|
||||
#define CONFIG_JOINT_STEREO 0x10 /* joint stereo */
|
||||
#define CONFIG_CROSS_DECORR 0x20 /* no-delay cross decorrelation */
|
||||
#define CONFIG_HYBRID_SHAPE 0x40 /* noise shape (hybrid mode only) */
|
||||
#define CONFIG_FLOAT_DATA 0x80 /* ieee 32-bit floating point data */
|
||||
|
||||
#define CONFIG_FAST_FLAG 0x200 /* fast mode */
|
||||
#define CONFIG_HIGH_FLAG 0x800 /* high quality mode */
|
||||
#define CONFIG_VERY_HIGH_FLAG 0x1000 /* very high */
|
||||
#define CONFIG_BITRATE_KBPS 0x2000 /* bitrate is kbps, not bits / sample */
|
||||
#define CONFIG_AUTO_SHAPING 0x4000 /* automatic noise shaping */
|
||||
#define CONFIG_SHAPE_OVERRIDE 0x8000 /* shaping mode specified */
|
||||
#define CONFIG_JOINT_OVERRIDE 0x10000 /* joint-stereo mode specified */
|
||||
#define CONFIG_CREATE_EXE 0x40000 /* create executable */
|
||||
#define CONFIG_CREATE_WVC 0x80000 /* create correction file */
|
||||
#define CONFIG_OPTIMIZE_WVC 0x100000 /* maximize bybrid compression */
|
||||
#define CONFIG_CALC_NOISE 0x800000 /* calc noise in hybrid mode */
|
||||
#define CONFIG_LOSSY_MODE 0x1000000 /* obsolete (for information) */
|
||||
#define CONFIG_EXTRA_MODE 0x2000000 /* extra processing mode */
|
||||
#define CONFIG_SKIP_WVX 0x4000000 /* no wvx stream w/ floats & big ints */
|
||||
#define CONFIG_MD5_CHECKSUM 0x8000000 /* compute & store MD5 signature */
|
||||
#define CONFIG_OPTIMIZE_MONO 0x80000000 /* optimize for mono streams posing as stereo */
|
||||
|
||||
/*////////////////////////////// WavPack Stream /////////////////////////////// */
|
||||
|
||||
/* This internal structure contains everything required to handle a WavPack */
|
||||
/* "stream", which is defined as a stereo or mono stream of audio samples. For */
|
||||
/* multichannel audio several of these would be required. Each stream contains */
|
||||
/* pointers to hold a complete allocated block of WavPack data, although it's */
|
||||
/* possible to decode WavPack blocks without buffering an entire block. */
|
||||
|
||||
typedef int32_t (*read_stream)(void *, int32_t);
|
||||
|
||||
typedef struct bs {
|
||||
uchar *buf, *end, *ptr;
|
||||
void (*wrap)(struct bs *bs);
|
||||
uint32_t file_bytes, sr;
|
||||
int error, bc;
|
||||
read_stream file;
|
||||
} Bitstream;
|
||||
|
||||
#define MAX_NTERMS 16
|
||||
#define MAX_TERM 8
|
||||
|
||||
struct decorr_pass {
|
||||
short term, delta, weight_A, weight_B;
|
||||
int32_t samples_A [MAX_TERM], samples_B [MAX_TERM];
|
||||
};
|
||||
|
||||
struct entropy_data {
|
||||
uint32_t median [3], slow_level, error_limit;
|
||||
};
|
||||
|
||||
struct words_data {
|
||||
uint32_t bitrate_delta [2], bitrate_acc [2];
|
||||
uint32_t pend_data, holding_one, zeros_acc;
|
||||
int holding_zero, pend_count;
|
||||
struct entropy_data c [2];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
WavpackHeader wphdr;
|
||||
Bitstream wvbits;
|
||||
|
||||
struct words_data w;
|
||||
|
||||
int num_terms, mute_error;
|
||||
uint32_t sample_index, crc;
|
||||
|
||||
uchar int32_sent_bits, int32_zeros, int32_ones, int32_dups;
|
||||
uchar float_flags, float_shift, float_max_exp, float_norm_exp;
|
||||
|
||||
struct decorr_pass decorr_passes [MAX_NTERMS];
|
||||
|
||||
} WavpackStream;
|
||||
|
||||
/* flags for float_flags: */
|
||||
|
||||
#define FLOAT_SHIFT_ONES 1 /* bits left-shifted into float = '1' */
|
||||
#define FLOAT_SHIFT_SAME 2 /* bits left-shifted into float are the same */
|
||||
#define FLOAT_SHIFT_SENT 4 /* bits shifted into float are sent literally */
|
||||
#define FLOAT_ZEROS_SENT 8 /* "zeros" are not all real zeros */
|
||||
#define FLOAT_NEG_ZEROS 0x10 /* contains negative zeros */
|
||||
#define FLOAT_EXCEPTIONS 0x20 /* contains exceptions (inf, nan, etc.) */
|
||||
|
||||
/*///////////////////////////// WavPack Context /////////////////////////////// */
|
||||
|
||||
/* This internal structure holds everything required to encode or decode WavPack */
|
||||
/* files. It is recommended that direct access to this structure be minimized */
|
||||
/* and the provided utilities used instead. */
|
||||
|
||||
typedef struct {
|
||||
WavpackConfig config;
|
||||
WavpackStream stream;
|
||||
|
||||
uchar read_buffer [1024];
|
||||
char error_message [80];
|
||||
|
||||
read_stream infile;
|
||||
uint32_t total_samples, crc_errors, first_flags;
|
||||
int open_flags, norm_offset, reduced_channels, lossy_blocks;
|
||||
|
||||
} WavpackContext;
|
||||
|
||||
/*////////////////////// function prototypes and macros ////////////////////// */
|
||||
|
||||
#define CLEAR(destin) memset (&destin, 0, sizeof (destin));
|
||||
|
||||
/* bits.c */
|
||||
|
||||
void bs_open_read (Bitstream *bs, uchar *buffer_start, uchar *buffer_end, read_stream file, uint32_t file_bytes);
|
||||
|
||||
#define bs_is_open(bs) ((bs)->ptr != NULL)
|
||||
|
||||
#define getbit(bs) ( \
|
||||
(((bs)->bc) ? \
|
||||
((bs)->bc--, (bs)->sr & 1) : \
|
||||
(((++((bs)->ptr) != (bs)->end) ? (void) 0 : (bs)->wrap (bs)), (bs)->bc = 7, ((bs)->sr = *((bs)->ptr)) & 1) \
|
||||
) ? \
|
||||
((bs)->sr >>= 1, 1) : \
|
||||
((bs)->sr >>= 1, 0) \
|
||||
)
|
||||
|
||||
#define getbits(value, nbits, bs) { \
|
||||
while ((nbits) > (bs)->bc) { \
|
||||
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
|
||||
(bs)->sr |= (int32_t)*((bs)->ptr) << (bs)->bc; \
|
||||
(bs)->bc += 8; \
|
||||
} \
|
||||
*(value) = (bs)->sr; \
|
||||
if ((bs)->bc > 32) { \
|
||||
(bs)->bc -= (nbits); \
|
||||
(bs)->sr = *((bs)->ptr) >> (8 - (bs)->bc); \
|
||||
} \
|
||||
else { \
|
||||
(bs)->bc -= (nbits); \
|
||||
(bs)->sr >>= (nbits); \
|
||||
} \
|
||||
}
|
||||
|
||||
void little_endian_to_native (void *data, char *format);
|
||||
void native_to_little_endian (void *data, char *format);
|
||||
|
||||
/* These macros implement the weight application and update operations */
|
||||
/* that are at the heart of the decorrelation loops. Note that when there */
|
||||
/* are several alternative versions of the same macro (marked with PERFCOND) */
|
||||
/* then the versions are functionally equivalent with respect to WavPack */
|
||||
/* decoding and the user should choose the one that provides the best */
|
||||
/* performance. This may be easier to check when NOT using the assembly */
|
||||
/* language optimizations. */
|
||||
|
||||
#if 1 /* PERFCOND */
|
||||
#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10)
|
||||
#else
|
||||
#define apply_weight_i(weight, sample) ((((weight * sample) >> 8) + 2) >> 2)
|
||||
#endif
|
||||
|
||||
#define apply_weight_f(weight, sample) (((((sample & 0xffffL) * weight) >> 9) + \
|
||||
(((sample & ~0xffffL) >> 9) * weight) + 1) >> 1)
|
||||
|
||||
#if 1 /* PERFCOND */
|
||||
#define apply_weight(weight, sample) (sample != (short) sample ? \
|
||||
apply_weight_f (weight, sample) : apply_weight_i (weight, sample))
|
||||
#else
|
||||
#define apply_weight(weight, sample) ((int32_t)((weight * (int64_t) sample + 512) >> 10))
|
||||
#endif
|
||||
|
||||
#if 0 /* PERFCOND */
|
||||
#define update_weight(weight, delta, source, result) \
|
||||
if (source && result) { int32_t s = (int32_t) (source ^ result) >> 31; weight = (delta ^ s) + (weight - s); }
|
||||
#elif 1
|
||||
#define update_weight(weight, delta, source, result) \
|
||||
if (source && result) weight += (((source ^ result) >> 30) | 1) * delta
|
||||
#else
|
||||
#define update_weight(weight, delta, source, result) \
|
||||
if (source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta)
|
||||
#endif
|
||||
|
||||
#define update_weight_clip(weight, delta, source, result) \
|
||||
if (source && result && ((source ^ result) < 0 ? (weight -= delta) < -1024 : (weight += delta) > 1024)) \
|
||||
weight = weight < 0 ? -1024 : 1024
|
||||
|
||||
/* unpack.c */
|
||||
|
||||
int unpack_init (WavpackContext *wpc);
|
||||
int init_wv_bitstream (WavpackContext *wpc, WavpackMetadata *wpmd);
|
||||
int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd);
|
||||
int read_config_info (WavpackContext *wpc, WavpackMetadata *wpmd);
|
||||
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count);
|
||||
int check_crc_error (WavpackContext *wpc);
|
||||
|
||||
/* metadata.c stuff */
|
||||
|
||||
int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd);
|
||||
int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd);
|
||||
|
||||
/* words.c stuff */
|
||||
|
||||
int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags,
|
||||
struct words_data *w, Bitstream *bs);
|
||||
int32_t exp2s (int log);
|
||||
int restore_weight (signed char weight);
|
||||
|
||||
#define WORD_EOF (1L << 31)
|
||||
|
||||
/* float.c */
|
||||
|
||||
int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd);
|
||||
void float_values (WavpackStream *wps, int32_t *values, int32_t num_values);
|
||||
|
||||
/* wputils.c */
|
||||
|
||||
WavpackContext *WavpackOpenFileInput (read_stream infile, char *error);
|
||||
|
||||
int WavpackGetMode (WavpackContext *wpc);
|
||||
|
||||
#define MODE_WVC 0x1
|
||||
#define MODE_LOSSLESS 0x2
|
||||
#define MODE_HYBRID 0x4
|
||||
#define MODE_FLOAT 0x8
|
||||
#define MODE_VALID_TAG 0x10
|
||||
#define MODE_HIGH 0x20
|
||||
#define MODE_FAST 0x40
|
||||
|
||||
uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples);
|
||||
uint32_t WavpackGetNumSamples (WavpackContext *wpc);
|
||||
uint32_t WavpackGetSampleIndex (WavpackContext *wpc);
|
||||
int WavpackGetNumErrors (WavpackContext *wpc);
|
||||
int WavpackLossyBlocks (WavpackContext *wpc);
|
||||
uint32_t WavpackGetSampleRate (WavpackContext *wpc);
|
||||
int WavpackGetBitsPerSample (WavpackContext *wpc);
|
||||
int WavpackGetBytesPerSample (WavpackContext *wpc);
|
||||
int WavpackGetNumChannels (WavpackContext *wpc);
|
||||
int WavpackGetReducedChannels (WavpackContext *wpc);
|
||||
@@ -1,560 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// words.c
|
||||
|
||||
// This module provides entropy word encoding and decoding functions using
|
||||
// a variation on the Rice method. This was introduced in version 3.93
|
||||
// because it allows splitting the data into a "lossy" stream and a
|
||||
// "correction" stream in a very efficient manner and is therefore ideal
|
||||
// for the "hybrid" mode. For 4.0, the efficiency of this method was
|
||||
// significantly improved by moving away from the normal Rice restriction of
|
||||
// using powers of two for the modulus divisions and now the method can be
|
||||
// used for both hybrid and pure lossless encoding.
|
||||
|
||||
// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%),
|
||||
// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the
|
||||
// previous. Using standard Rice coding on this data would result in 1.4
|
||||
// bits per sample average (not counting sign bit). However, there is a
|
||||
// very simple encoding that is over 99% efficient with this data and
|
||||
// results in about 1.22 bits per sample.
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//////////////////////////////// local macros /////////////////////////////////
|
||||
|
||||
#define LIMIT_ONES 16 // maximum consecutive 1s sent for "div" data
|
||||
|
||||
// these control the time constant "slow_level" which is used for hybrid mode
|
||||
// that controls bitrate as a function of residual level (HYBRID_BITRATE).
|
||||
#define SLS 8
|
||||
#define SLO ((1 << (SLS - 1)))
|
||||
|
||||
// these control the time constant of the 3 median level breakpoints
|
||||
#define DIV0 128 // 5/7 of samples
|
||||
#define DIV1 64 // 10/49 of samples
|
||||
#define DIV2 32 // 20/343 of samples
|
||||
|
||||
// this macro retrieves the specified median breakpoint (without frac; min = 1)
|
||||
#define GET_MED(med) (((c->median [med]) >> 4) + 1)
|
||||
|
||||
// These macros update the specified median breakpoints. Note that the median
|
||||
// is incremented when the sample is higher than the median, else decremented.
|
||||
// They are designed so that the median will never drop below 1 and the value
|
||||
// is essentially stationary if there are 2 increments for every 5 decrements.
|
||||
|
||||
#define INC_MED0() (c->median [0] += ((c->median [0] + DIV0) / DIV0) * 5)
|
||||
#define DEC_MED0() (c->median [0] -= ((c->median [0] + (DIV0-2)) / DIV0) * 2)
|
||||
#define INC_MED1() (c->median [1] += ((c->median [1] + DIV1) / DIV1) * 5)
|
||||
#define DEC_MED1() (c->median [1] -= ((c->median [1] + (DIV1-2)) / DIV1) * 2)
|
||||
#define INC_MED2() (c->median [2] += ((c->median [2] + DIV2) / DIV2) * 5)
|
||||
#define DEC_MED2() (c->median [2] -= ((c->median [2] + (DIV2-2)) / DIV2) * 2)
|
||||
|
||||
#define count_bits(av) ( \
|
||||
(av) < (1 << 8) ? nbits_table [av] : \
|
||||
( \
|
||||
(av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \
|
||||
((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \
|
||||
) \
|
||||
)
|
||||
|
||||
///////////////////////////// local table storage ////////////////////////////
|
||||
|
||||
const char nbits_table [] = {
|
||||
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, // 0 - 15
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 16 - 31
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 32 - 47
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 48 - 63
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 64 - 79
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 95
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96 - 111
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 112 - 127
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 128 - 143
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 144 - 159
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160 - 175
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 176 - 191
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192 - 207
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 208 - 223
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224 - 239
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 // 240 - 255
|
||||
};
|
||||
|
||||
static const uchar log2_table [] = {
|
||||
0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
|
||||
0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a,
|
||||
0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
|
||||
0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
|
||||
0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
|
||||
0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
|
||||
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
|
||||
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2,
|
||||
0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0,
|
||||
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce,
|
||||
0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb,
|
||||
0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7,
|
||||
0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4,
|
||||
0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff
|
||||
};
|
||||
|
||||
static const uchar exp2_table [] = {
|
||||
0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b,
|
||||
0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16,
|
||||
0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
|
||||
0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
|
||||
0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
|
||||
0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
|
||||
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4,
|
||||
0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9,
|
||||
0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
|
||||
};
|
||||
|
||||
static const char ones_count_table [] = {
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
|
||||
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8
|
||||
};
|
||||
|
||||
///////////////////////////// executable code ////////////////////////////////
|
||||
|
||||
void init_words (WavpackStream *wps)
|
||||
{
|
||||
CLEAR (wps->w);
|
||||
}
|
||||
|
||||
static int mylog2 (uint32_t avalue);
|
||||
|
||||
// Read the median log2 values from the specifed metadata structure, convert
|
||||
// them back to 32-bit unsigned values and store them. If length is not
|
||||
// exactly correct then we flag and return an error.
|
||||
|
||||
int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
uchar *byteptr = wpmd->data;
|
||||
|
||||
if (wpmd->byte_length != ((wps->wphdr.flags & MONO_DATA) ? 6 : 12))
|
||||
return FALSE;
|
||||
|
||||
wps->w.c [0].median [0] = exp2s (byteptr [0] + (byteptr [1] << 8));
|
||||
wps->w.c [0].median [1] = exp2s (byteptr [2] + (byteptr [3] << 8));
|
||||
wps->w.c [0].median [2] = exp2s (byteptr [4] + (byteptr [5] << 8));
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
wps->w.c [1].median [0] = exp2s (byteptr [6] + (byteptr [7] << 8));
|
||||
wps->w.c [1].median [1] = exp2s (byteptr [8] + (byteptr [9] << 8));
|
||||
wps->w.c [1].median [2] = exp2s (byteptr [10] + (byteptr [11] << 8));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Read the hybrid related values from the specifed metadata structure, convert
|
||||
// them back to their internal formats and store them. The extended profile
|
||||
// stuff is not implemented yet, so return an error if we get more data than
|
||||
// we know what to do with.
|
||||
|
||||
int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
|
||||
{
|
||||
uchar *byteptr = wpmd->data;
|
||||
uchar *endptr = byteptr + wpmd->byte_length;
|
||||
|
||||
if (wps->wphdr.flags & HYBRID_BITRATE) {
|
||||
wps->w.c [0].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8));
|
||||
byteptr += 2;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
wps->w.c [1].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8));
|
||||
byteptr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
|
||||
byteptr += 2;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
wps->w.bitrate_acc [1] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
|
||||
byteptr += 2;
|
||||
}
|
||||
|
||||
if (byteptr < endptr) {
|
||||
wps->w.bitrate_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
byteptr += 2;
|
||||
|
||||
if (!(wps->wphdr.flags & MONO_DATA)) {
|
||||
wps->w.bitrate_delta [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
|
||||
byteptr += 2;
|
||||
}
|
||||
|
||||
if (byteptr < endptr)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// This function is called during both encoding and decoding of hybrid data to
|
||||
// update the "error_limit" variable which determines the maximum sample error
|
||||
// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only
|
||||
// currently implemented) this is calculated from the slow_level values and the
|
||||
// bitrate accumulators. Note that the bitrate accumulators can be changing.
|
||||
|
||||
void update_error_limit (struct words_data *w, uint32_t flags)
|
||||
{
|
||||
int bitrate_0 = (w->bitrate_acc [0] += w->bitrate_delta [0]) >> 16;
|
||||
|
||||
if (flags & MONO_DATA) {
|
||||
if (flags & HYBRID_BITRATE) {
|
||||
int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS;
|
||||
|
||||
if (slow_log_0 - bitrate_0 > -0x100)
|
||||
w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100);
|
||||
else
|
||||
w->c [0].error_limit = 0;
|
||||
}
|
||||
else
|
||||
w->c [0].error_limit = exp2s (bitrate_0);
|
||||
}
|
||||
else {
|
||||
int bitrate_1 = (w->bitrate_acc [1] += w->bitrate_delta [1]) >> 16;
|
||||
|
||||
if (flags & HYBRID_BITRATE) {
|
||||
int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS;
|
||||
int slow_log_1 = (w->c [1].slow_level + SLO) >> SLS;
|
||||
|
||||
if (flags & HYBRID_BALANCE) {
|
||||
int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1;
|
||||
|
||||
if (balance > bitrate_0) {
|
||||
bitrate_1 = bitrate_0 * 2;
|
||||
bitrate_0 = 0;
|
||||
}
|
||||
else if (-balance > bitrate_0) {
|
||||
bitrate_0 = bitrate_0 * 2;
|
||||
bitrate_1 = 0;
|
||||
}
|
||||
else {
|
||||
bitrate_1 = bitrate_0 + balance;
|
||||
bitrate_0 = bitrate_0 - balance;
|
||||
}
|
||||
}
|
||||
|
||||
if (slow_log_0 - bitrate_0 > -0x100)
|
||||
w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100);
|
||||
else
|
||||
w->c [0].error_limit = 0;
|
||||
|
||||
if (slow_log_1 - bitrate_1 > -0x100)
|
||||
w->c [1].error_limit = exp2s (slow_log_1 - bitrate_1 + 0x100);
|
||||
else
|
||||
w->c [1].error_limit = 0;
|
||||
}
|
||||
else {
|
||||
w->c [0].error_limit = exp2s (bitrate_0);
|
||||
w->c [1].error_limit = exp2s (bitrate_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t read_code (Bitstream *bs, uint32_t maxcode);
|
||||
|
||||
// Read the next word from the bitstream "wvbits" and return the value. This
|
||||
// function can be used for hybrid or lossless streams, but since an
|
||||
// optimized version is available for lossless this function would normally
|
||||
// be used for hybrid only. If a hybrid lossless stream is being read then
|
||||
// the "correction" offset is written at the specified pointer. A return value
|
||||
// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or
|
||||
// some other error occurred.
|
||||
|
||||
int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags,
|
||||
struct words_data *w, Bitstream *bs)
|
||||
{
|
||||
register struct entropy_data *c = w->c;
|
||||
int csamples;
|
||||
|
||||
if (!(flags & MONO_DATA))
|
||||
nsamples *= 2;
|
||||
|
||||
for (csamples = 0; csamples < nsamples; ++csamples) {
|
||||
uint32_t ones_count, low, mid, high;
|
||||
|
||||
if (!(flags & MONO_DATA))
|
||||
c = w->c + (csamples & 1);
|
||||
|
||||
if (!(w->c [0].median [0] & ~1) && !w->holding_zero && !w->holding_one && !(w->c [1].median [0] & ~1)) {
|
||||
uint32_t mask;
|
||||
int cbits;
|
||||
|
||||
if (w->zeros_acc) {
|
||||
if (--w->zeros_acc) {
|
||||
c->slow_level -= (c->slow_level + SLO) >> SLS;
|
||||
*buffer++ = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
|
||||
|
||||
if (cbits == 33)
|
||||
break;
|
||||
|
||||
if (cbits < 2)
|
||||
w->zeros_acc = cbits;
|
||||
else {
|
||||
for (mask = 1, w->zeros_acc = 0; --cbits; mask <<= 1)
|
||||
if (getbit (bs))
|
||||
w->zeros_acc |= mask;
|
||||
|
||||
w->zeros_acc |= mask;
|
||||
}
|
||||
|
||||
if (w->zeros_acc) {
|
||||
c->slow_level -= (c->slow_level + SLO) >> SLS;
|
||||
CLEAR (w->c [0].median);
|
||||
CLEAR (w->c [1].median);
|
||||
*buffer++ = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (w->holding_zero)
|
||||
ones_count = w->holding_zero = 0;
|
||||
else {
|
||||
int next8;
|
||||
|
||||
if (bs->bc < 8) {
|
||||
if (++(bs->ptr) == bs->end)
|
||||
bs->wrap (bs);
|
||||
|
||||
next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff;
|
||||
bs->bc += 8;
|
||||
}
|
||||
else
|
||||
next8 = bs->sr & 0xff;
|
||||
|
||||
if (next8 == 0xff) {
|
||||
bs->bc -= 8;
|
||||
bs->sr >>= 8;
|
||||
|
||||
for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count);
|
||||
|
||||
if (ones_count == (LIMIT_ONES + 1))
|
||||
break;
|
||||
|
||||
if (ones_count == LIMIT_ONES) {
|
||||
uint32_t mask;
|
||||
int cbits;
|
||||
|
||||
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
|
||||
|
||||
if (cbits == 33)
|
||||
break;
|
||||
|
||||
if (cbits < 2)
|
||||
ones_count = cbits;
|
||||
else {
|
||||
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
|
||||
if (getbit (bs))
|
||||
ones_count |= mask;
|
||||
|
||||
ones_count |= mask;
|
||||
}
|
||||
|
||||
ones_count += LIMIT_ONES;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bs->bc -= (ones_count = ones_count_table [next8]) + 1;
|
||||
bs->sr >>= ones_count + 1;
|
||||
}
|
||||
|
||||
if (w->holding_one) {
|
||||
w->holding_one = ones_count & 1;
|
||||
ones_count = (ones_count >> 1) + 1;
|
||||
}
|
||||
else {
|
||||
w->holding_one = ones_count & 1;
|
||||
ones_count >>= 1;
|
||||
}
|
||||
|
||||
w->holding_zero = ~w->holding_one & 1;
|
||||
}
|
||||
|
||||
if ((flags & HYBRID_FLAG) && ((flags & MONO_DATA) || !(csamples & 1)))
|
||||
update_error_limit (w, flags);
|
||||
|
||||
if (ones_count == 0) {
|
||||
low = 0;
|
||||
high = GET_MED (0) - 1;
|
||||
DEC_MED0 ();
|
||||
}
|
||||
else {
|
||||
low = GET_MED (0);
|
||||
INC_MED0 ();
|
||||
|
||||
if (ones_count == 1) {
|
||||
high = low + GET_MED (1) - 1;
|
||||
DEC_MED1 ();
|
||||
}
|
||||
else {
|
||||
low += GET_MED (1);
|
||||
INC_MED1 ();
|
||||
|
||||
if (ones_count == 2) {
|
||||
high = low + GET_MED (2) - 1;
|
||||
DEC_MED2 ();
|
||||
}
|
||||
else {
|
||||
low += (ones_count - 2) * GET_MED (2);
|
||||
high = low + GET_MED (2) - 1;
|
||||
INC_MED2 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mid = (high + low + 1) >> 1;
|
||||
|
||||
if (!c->error_limit)
|
||||
mid = read_code (bs, high - low) + low;
|
||||
else while (high - low > c->error_limit) {
|
||||
if (getbit (bs))
|
||||
mid = (high + (low = mid) + 1) >> 1;
|
||||
else
|
||||
mid = ((high = mid - 1) + low + 1) >> 1;
|
||||
}
|
||||
|
||||
*buffer++ = getbit (bs) ? ~mid : mid;
|
||||
|
||||
if (flags & HYBRID_BITRATE)
|
||||
c->slow_level = c->slow_level - ((c->slow_level + SLO) >> SLS) + mylog2 (mid);
|
||||
}
|
||||
|
||||
return (flags & MONO_DATA) ? csamples : (csamples / 2);
|
||||
}
|
||||
|
||||
// Read a single unsigned value from the specified bitstream with a value
|
||||
// from 0 to maxcode. If there are exactly a power of two number of possible
|
||||
// codes then this will read a fixed number of bits; otherwise it reads the
|
||||
// minimum number of bits and then determines whether another bit is needed
|
||||
// to define the code.
|
||||
|
||||
static uint32_t read_code (Bitstream *bs, uint32_t maxcode)
|
||||
{
|
||||
int bitcount = count_bits (maxcode);
|
||||
uint32_t extras = (1L << bitcount) - maxcode - 1, code;
|
||||
|
||||
if (!bitcount)
|
||||
return 0;
|
||||
|
||||
getbits (&code, bitcount - 1, bs);
|
||||
code &= (1L << (bitcount - 1)) - 1;
|
||||
|
||||
if (code >= extras) {
|
||||
code = (code << 1) - extras;
|
||||
|
||||
if (getbit (bs))
|
||||
++code;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// The concept of a base 2 logarithm is used in many parts of WavPack. It is
|
||||
// a way of sufficiently accurately representing 32-bit signed and unsigned
|
||||
// values storing only 16 bits (actually fewer). It is also used in the hybrid
|
||||
// mode for quickly comparing the relative magnitude of large values (i.e.
|
||||
// division) and providing smooth exponentials using only addition.
|
||||
|
||||
// These are not strict logarithms in that they become linear around zero and
|
||||
// can therefore represent both zero and negative values. They have 8 bits
|
||||
// of precision and in "roundtrip" conversions the total error never exceeds 1
|
||||
// part in 225 except for the cases of +/-115 and +/-195 (which error by 1).
|
||||
|
||||
|
||||
// This function returns the log2 for the specified 32-bit unsigned value.
|
||||
// The maximum value allowed is about 0xff800000 and returns 8447.
|
||||
|
||||
static int mylog2 (uint32_t avalue)
|
||||
{
|
||||
int dbits;
|
||||
|
||||
if ((avalue += avalue >> 9) < (1 << 8)) {
|
||||
dbits = nbits_table [avalue];
|
||||
return (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
|
||||
}
|
||||
else {
|
||||
if (avalue < (1L << 16))
|
||||
dbits = nbits_table [avalue >> 8] + 8;
|
||||
else if (avalue < (1L << 24))
|
||||
dbits = nbits_table [avalue >> 16] + 16;
|
||||
else
|
||||
dbits = nbits_table [avalue >> 24] + 24;
|
||||
|
||||
return (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
|
||||
}
|
||||
}
|
||||
|
||||
// This function returns the log2 for the specified 32-bit signed value.
|
||||
// All input values are valid and the return values are in the range of
|
||||
// +/- 8192.
|
||||
|
||||
int log2s (int32_t value)
|
||||
{
|
||||
return (value < 0) ? -mylog2 (-value) : mylog2 (value);
|
||||
}
|
||||
|
||||
// This function returns the original integer represented by the supplied
|
||||
// logarithm (at least within the provided accuracy). The log is signed,
|
||||
// but since a full 32-bit value is returned this can be used for unsigned
|
||||
// conversions as well (i.e. the input range is -8192 to +8447).
|
||||
|
||||
int32_t exp2s (int log)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
if (log < 0)
|
||||
return -exp2s (-log);
|
||||
|
||||
value = exp2_table [log & 0xff] | 0x100;
|
||||
|
||||
if ((log >>= 8) <= 9)
|
||||
return value >> (9 - log);
|
||||
else
|
||||
return value << (log - 9);
|
||||
}
|
||||
|
||||
// These two functions convert internal weights (which are normally +/-1024)
|
||||
// to and from an 8-bit signed character version for storage in metadata. The
|
||||
// weights are clipped here in the case that they are outside that range.
|
||||
|
||||
int restore_weight (signed char weight)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = (int) weight << 3) > 0)
|
||||
result += (result + 64) >> 7;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wputils.c
|
||||
|
||||
// This module provides a high-level interface for decoding WavPack 4.0 audio
|
||||
// streams and files. WavPack data is read with a stream reading callback. No
|
||||
// direct seeking is provided for, but it is possible to start decoding
|
||||
// anywhere in a WavPack stream. In this case, WavPack will be able to provide
|
||||
// the sample-accurate position when it synchs with the data and begins
|
||||
// decoding.
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
///////////////////////////// local table storage ////////////////////////////
|
||||
|
||||
const uint32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
|
||||
24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
|
||||
|
||||
///////////////////////////// executable code ////////////////////////////////
|
||||
|
||||
static uint32_t read_next_header (read_stream infile, WavpackHeader *wphdr);
|
||||
|
||||
// This function reads data from the specified stream in search of a valid
|
||||
// WavPack 4.0 audio block. If this fails in 1 megabyte (or an invalid or
|
||||
// unsupported WavPack block is encountered) then an appropriate message is
|
||||
// copied to "error" and NULL is returned, otherwise a pointer to a
|
||||
// WavpackContext structure is returned (which is used to call all other
|
||||
// functions in this module). This can be initiated at the beginning of a
|
||||
// WavPack file, or anywhere inside a WavPack file. To determine the exact
|
||||
// position within the file use WavpackGetSampleIndex(). For demonstration
|
||||
// purposes this uses a single static copy of the WavpackContext structure,
|
||||
// so obviously it cannot be used for more than one file at a time. Also,
|
||||
// this function will not handle "correction" files, plays only the first
|
||||
// two channels of multi-channel files, and is limited in resolution in some
|
||||
// large integer or floating point files (but always provides at least 24 bits
|
||||
// of resolution).
|
||||
|
||||
static WavpackContext wpc;
|
||||
|
||||
WavpackContext *WavpackOpenFileInput (read_stream infile, char *error)
|
||||
{
|
||||
WavpackStream *wps = &wpc.stream;
|
||||
uint32_t bcount;
|
||||
|
||||
CLEAR (wpc);
|
||||
wpc.infile = infile;
|
||||
wpc.total_samples = (uint32_t) -1;
|
||||
wpc.norm_offset = 0;
|
||||
wpc.open_flags = 0;
|
||||
|
||||
// open the source file for reading and store the size
|
||||
|
||||
while (!wps->wphdr.block_samples) {
|
||||
|
||||
bcount = read_next_header (wpc.infile, &wps->wphdr);
|
||||
|
||||
if (bcount == (uint32_t) -1) {
|
||||
strcpy (error, "not compatible with this version of WavPack file!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wps->wphdr.block_samples && wps->wphdr.total_samples != (uint32_t) -1)
|
||||
wpc.total_samples = wps->wphdr.total_samples;
|
||||
|
||||
if (!unpack_init (&wpc)) {
|
||||
strcpy (error, wpc.error_message [0] ? wpc.error_message :
|
||||
"not compatible with this version of WavPack file!");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
wpc.config.flags &= ~0xff;
|
||||
wpc.config.flags |= wps->wphdr.flags & 0xff;
|
||||
wpc.config.bytes_per_sample = (wps->wphdr.flags & BYTES_STORED) + 1;
|
||||
wpc.config.float_norm_exp = wps->float_norm_exp;
|
||||
|
||||
wpc.config.bits_per_sample = (wpc.config.bytes_per_sample * 8) -
|
||||
((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB);
|
||||
|
||||
if (wpc.config.flags & FLOAT_DATA) {
|
||||
wpc.config.bytes_per_sample = 3;
|
||||
wpc.config.bits_per_sample = 24;
|
||||
}
|
||||
|
||||
if (!wpc.config.sample_rate) {
|
||||
if (!wps || !wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK)
|
||||
wpc.config.sample_rate = 44100;
|
||||
else
|
||||
wpc.config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB];
|
||||
}
|
||||
|
||||
if (!wpc.config.num_channels) {
|
||||
wpc.config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
|
||||
wpc.config.channel_mask = 0x5 - wpc.config.num_channels;
|
||||
}
|
||||
|
||||
if (!(wps->wphdr.flags & FINAL_BLOCK))
|
||||
wpc.reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
|
||||
|
||||
return &wpc;
|
||||
}
|
||||
|
||||
// This function obtains general information about an open file and returns
|
||||
// a mask with the following bit values:
|
||||
|
||||
// MODE_LOSSLESS: file is lossless (pure lossless only)
|
||||
// MODE_HYBRID: file is hybrid mode (lossy part only)
|
||||
// MODE_FLOAT: audio data is 32-bit ieee floating point (but will provided
|
||||
// in 24-bit integers for convenience)
|
||||
// MODE_HIGH: file was created in "high" mode (information only)
|
||||
// MODE_FAST: file was created in "fast" mode (information only)
|
||||
|
||||
int WavpackGetMode (WavpackContext *wpc)
|
||||
{
|
||||
int mode = 0;
|
||||
|
||||
if (wpc) {
|
||||
if (wpc->config.flags & CONFIG_HYBRID_FLAG)
|
||||
mode |= MODE_HYBRID;
|
||||
else if (!(wpc->config.flags & CONFIG_LOSSY_MODE))
|
||||
mode |= MODE_LOSSLESS;
|
||||
|
||||
if (wpc->lossy_blocks)
|
||||
mode &= ~MODE_LOSSLESS;
|
||||
|
||||
if (wpc->config.flags & CONFIG_FLOAT_DATA)
|
||||
mode |= MODE_FLOAT;
|
||||
|
||||
if (wpc->config.flags & CONFIG_HIGH_FLAG)
|
||||
mode |= MODE_HIGH;
|
||||
|
||||
if (wpc->config.flags & CONFIG_FAST_FLAG)
|
||||
mode |= MODE_FAST;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
// Unpack the specified number of samples from the current file position.
|
||||
// Note that "samples" here refers to "complete" samples, which would be
|
||||
// 2 longs for stereo files. The audio data is returned right-justified in
|
||||
// 32-bit longs in the endian mode native to the executing processor. So,
|
||||
// if the original data was 16-bit, then the values returned would be
|
||||
// +/-32k. Floating point data will be returned as 24-bit integers (and may
|
||||
// also be clipped). The actual number of samples unpacked is returned,
|
||||
// which should be equal to the number requested unless the end of fle is
|
||||
// encountered or an error occurs.
|
||||
|
||||
uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples)
|
||||
{
|
||||
WavpackStream *wps = &wpc->stream;
|
||||
uint32_t bcount, samples_unpacked = 0, samples_to_unpack;
|
||||
int num_channels = wpc->config.num_channels;
|
||||
|
||||
while (samples) {
|
||||
if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
|
||||
wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) {
|
||||
bcount = read_next_header (wpc->infile, &wps->wphdr);
|
||||
|
||||
if (bcount == (uint32_t) -1)
|
||||
break;
|
||||
|
||||
if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index)
|
||||
if (!unpack_init (wpc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
|
||||
wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples)
|
||||
continue;
|
||||
|
||||
if (wps->sample_index < wps->wphdr.block_index) {
|
||||
samples_to_unpack = wps->wphdr.block_index - wps->sample_index;
|
||||
|
||||
if (samples_to_unpack > samples)
|
||||
samples_to_unpack = samples;
|
||||
|
||||
wps->sample_index += samples_to_unpack;
|
||||
samples_unpacked += samples_to_unpack;
|
||||
samples -= samples_to_unpack;
|
||||
|
||||
if (wpc->reduced_channels)
|
||||
samples_to_unpack *= wpc->reduced_channels;
|
||||
else
|
||||
samples_to_unpack *= num_channels;
|
||||
|
||||
while (samples_to_unpack--)
|
||||
*buffer++ = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
samples_to_unpack = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index;
|
||||
|
||||
if (samples_to_unpack > samples)
|
||||
samples_to_unpack = samples;
|
||||
|
||||
unpack_samples (wpc, buffer, samples_to_unpack);
|
||||
|
||||
if (wpc->reduced_channels)
|
||||
buffer += samples_to_unpack * wpc->reduced_channels;
|
||||
else
|
||||
buffer += samples_to_unpack * num_channels;
|
||||
|
||||
samples_unpacked += samples_to_unpack;
|
||||
samples -= samples_to_unpack;
|
||||
|
||||
if (wps->sample_index == wps->wphdr.block_index + wps->wphdr.block_samples) {
|
||||
if (check_crc_error (wpc))
|
||||
wpc->crc_errors++;
|
||||
}
|
||||
|
||||
if (wps->sample_index == wpc->total_samples)
|
||||
break;
|
||||
}
|
||||
|
||||
return samples_unpacked;
|
||||
}
|
||||
|
||||
// Get total number of samples contained in the WavPack file, or -1 if unknown
|
||||
|
||||
uint32_t WavpackGetNumSamples (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->total_samples : (uint32_t) -1;
|
||||
}
|
||||
|
||||
// Get the current sample index position, or -1 if unknown
|
||||
|
||||
uint32_t WavpackGetSampleIndex (WavpackContext *wpc)
|
||||
{
|
||||
if (wpc)
|
||||
return wpc->stream.sample_index;
|
||||
|
||||
return (uint32_t) -1;
|
||||
}
|
||||
|
||||
// Get the number of errors encountered so far
|
||||
|
||||
int WavpackGetNumErrors (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->crc_errors : 0;
|
||||
}
|
||||
|
||||
// return TRUE if any uncorrected lossy blocks were actually written or read
|
||||
|
||||
int WavpackLossyBlocks (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->lossy_blocks : 0;
|
||||
}
|
||||
|
||||
// Returns the sample rate of the specified WavPack file
|
||||
|
||||
uint32_t WavpackGetSampleRate (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->config.sample_rate : 44100;
|
||||
}
|
||||
|
||||
// Returns the number of channels of the specified WavPack file. Note that
|
||||
// this is the actual number of channels contained in the file, but this
|
||||
// version can only decode the first two.
|
||||
|
||||
int WavpackGetNumChannels (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->config.num_channels : 2;
|
||||
}
|
||||
|
||||
// Returns the actual number of valid bits per sample contained in the
|
||||
// original file, which may or may not be a multiple of 8. Floating data
|
||||
// always has 32 bits, integers may be from 1 to 32 bits each. When this
|
||||
// value is not a multiple of 8, then the "extra" bits are located in the
|
||||
// LSBs of the results. That is, values are right justified when unpacked
|
||||
// into longs, but are left justified in the number of bytes used by the
|
||||
// original data.
|
||||
|
||||
int WavpackGetBitsPerSample (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->config.bits_per_sample : 16;
|
||||
}
|
||||
|
||||
// Returns the number of bytes used for each sample (1 to 4) in the original
|
||||
// file. This is required information for the user of this module because the
|
||||
// audio data is returned in the LOWER bytes of the long buffer and must be
|
||||
// left-shifted 8, 16, or 24 bits if normalized longs are required.
|
||||
|
||||
int WavpackGetBytesPerSample (WavpackContext *wpc)
|
||||
{
|
||||
return wpc ? wpc->config.bytes_per_sample : 2;
|
||||
}
|
||||
|
||||
// This function will return the actual number of channels decoded from the
|
||||
// file (which may or may not be less than the actual number of channels, but
|
||||
// will always be 1 or 2). Normally, this will be the front left and right
|
||||
// channels of a multi-channel file.
|
||||
|
||||
int WavpackGetReducedChannels (WavpackContext *wpc)
|
||||
{
|
||||
if (wpc)
|
||||
return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Read from current file position until a valid 32-byte WavPack 4.0 header is
|
||||
// found and read into the specified pointer. The number of bytes skipped is
|
||||
// returned. If no WavPack header is found within 1 meg, then a -1 is returned
|
||||
// to indicate the error. No additional bytes are read past the header and it
|
||||
// is returned in the processor's native endian mode. Seeking is not required.
|
||||
|
||||
static uint32_t read_next_header (read_stream infile, WavpackHeader *wphdr)
|
||||
{
|
||||
char buffer [sizeof (*wphdr)], *sp = buffer + sizeof (*wphdr), *ep = sp;
|
||||
uint32_t bytes_skipped = 0;
|
||||
int bleft;
|
||||
|
||||
while (1) {
|
||||
if (sp < ep) {
|
||||
bleft = ep - sp;
|
||||
memcpy (buffer, sp, bleft);
|
||||
}
|
||||
else
|
||||
bleft = 0;
|
||||
|
||||
if (infile (buffer + bleft, sizeof (*wphdr) - bleft) != (int32_t) sizeof (*wphdr) - bleft)
|
||||
return -1;
|
||||
|
||||
sp = buffer;
|
||||
|
||||
if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' &&
|
||||
!(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 &&
|
||||
sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff)) {
|
||||
memcpy (wphdr, buffer, sizeof (*wphdr));
|
||||
little_endian_to_native (wphdr, WavpackHeaderFormat);
|
||||
return bytes_skipped;
|
||||
}
|
||||
|
||||
while (sp < ep && *sp != 'w')
|
||||
sp++;
|
||||
|
||||
if ((bytes_skipped += sp - buffer) > 1048576L)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** WAVPACK **** //
|
||||
// Hybrid Lossless Wavefile Compressor //
|
||||
// Copyright (c) 1998 - 2006 Conifer Software. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wv_filter.c
|
||||
|
||||
// This is the main module for the demonstration WavPack command-line
|
||||
// decoder filter. It uses the tiny "hardware" version of the decoder and
|
||||
// accepts WavPack files on stdin and outputs a standard MS wav file to
|
||||
// stdout. Note that this involves converting the data to little-endian
|
||||
// (if the executing processor is not), possibly packing the data into
|
||||
// fewer bytes per sample, and generating an appropriate riff wav header.
|
||||
// Note that this is NOT the copy of the RIFF header that might be stored
|
||||
// in the file, and any additional RIFF information and tags are lost.
|
||||
// See wputils.c for further limitations.
|
||||
|
||||
#include "wavpack.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// These structures are used to place a wav riff header at the beginning of
|
||||
// the output.
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
char formType [4];
|
||||
} RiffChunkHeader;
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
} ChunkHeader;
|
||||
|
||||
#define ChunkHeaderFormat "4L"
|
||||
|
||||
typedef struct {
|
||||
ushort FormatTag, NumChannels;
|
||||
uint32_t SampleRate, BytesPerSecond;
|
||||
ushort BlockAlign, BitsPerSample;
|
||||
} WaveHeader;
|
||||
|
||||
#define WaveHeaderFormat "SSLLSS"
|
||||
|
||||
static uchar *format_samples (int bps, uchar *dst, int32_t *src, uint32_t samcnt);
|
||||
static int32_t read_bytes (void *buff, int32_t bcount);
|
||||
static int32_t temp_buffer [256];
|
||||
|
||||
int main ()
|
||||
{
|
||||
ChunkHeader FormatChunkHeader, DataChunkHeader;
|
||||
RiffChunkHeader RiffChunkHeader;
|
||||
WaveHeader WaveHeader;
|
||||
|
||||
uint32_t total_unpacked_samples = 0, total_samples;
|
||||
int num_channels, bps;
|
||||
WavpackContext *wpc;
|
||||
char error [80];
|
||||
|
||||
#if defined(WIN32)
|
||||
setmode (fileno (stdin), O_BINARY);
|
||||
setmode (fileno (stdout), O_BINARY);
|
||||
#endif
|
||||
|
||||
wpc = WavpackOpenFileInput (read_bytes, error);
|
||||
|
||||
if (!wpc) {
|
||||
fputs (error, stderr);
|
||||
fputs ("\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
num_channels = WavpackGetReducedChannels (wpc);
|
||||
total_samples = WavpackGetNumSamples (wpc);
|
||||
bps = WavpackGetBytesPerSample (wpc);
|
||||
|
||||
strncpy (RiffChunkHeader.ckID, "RIFF", sizeof (RiffChunkHeader.ckID));
|
||||
RiffChunkHeader.ckSize = total_samples * num_channels * bps + sizeof (ChunkHeader) * 2 + sizeof (WaveHeader) + 4;
|
||||
strncpy (RiffChunkHeader.formType, "WAVE", sizeof (RiffChunkHeader.formType));
|
||||
|
||||
strncpy (FormatChunkHeader.ckID, "fmt ", sizeof (FormatChunkHeader.ckID));
|
||||
FormatChunkHeader.ckSize = sizeof (WaveHeader);
|
||||
|
||||
WaveHeader.FormatTag = 1;
|
||||
WaveHeader.NumChannels = num_channels;
|
||||
WaveHeader.SampleRate = WavpackGetSampleRate (wpc);
|
||||
WaveHeader.BlockAlign = num_channels * bps;
|
||||
WaveHeader.BytesPerSecond = WaveHeader.SampleRate * WaveHeader.BlockAlign;
|
||||
WaveHeader.BitsPerSample = WavpackGetBitsPerSample (wpc);
|
||||
|
||||
strncpy (DataChunkHeader.ckID, "data", sizeof (DataChunkHeader.ckID));
|
||||
DataChunkHeader.ckSize = total_samples * num_channels * bps;
|
||||
|
||||
native_to_little_endian (&RiffChunkHeader, ChunkHeaderFormat);
|
||||
native_to_little_endian (&FormatChunkHeader, ChunkHeaderFormat);
|
||||
native_to_little_endian (&WaveHeader, WaveHeaderFormat);
|
||||
native_to_little_endian (&DataChunkHeader, ChunkHeaderFormat);
|
||||
|
||||
if (!fwrite (&RiffChunkHeader, sizeof (RiffChunkHeader), 1, stdout) ||
|
||||
!fwrite (&FormatChunkHeader, sizeof (FormatChunkHeader), 1, stdout) ||
|
||||
!fwrite (&WaveHeader, sizeof (WaveHeader), 1, stdout) ||
|
||||
!fwrite (&DataChunkHeader, sizeof (DataChunkHeader), 1, stdout)) {
|
||||
fputs ("can't write .WAV data, disk probably full!\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint32_t samples_unpacked;
|
||||
|
||||
samples_unpacked = WavpackUnpackSamples (wpc, temp_buffer, 256 / num_channels);
|
||||
total_unpacked_samples += samples_unpacked;
|
||||
|
||||
if (samples_unpacked) {
|
||||
format_samples (bps, (uchar *) temp_buffer, temp_buffer, samples_unpacked *= num_channels);
|
||||
|
||||
if (fwrite (temp_buffer, bps, samples_unpacked, stdout) != samples_unpacked) {
|
||||
fputs ("can't write .WAV data, disk probably full!\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!samples_unpacked)
|
||||
break;
|
||||
}
|
||||
|
||||
fflush (stdout);
|
||||
|
||||
if (WavpackGetNumSamples (wpc) != (uint32_t) -1 &&
|
||||
total_unpacked_samples != WavpackGetNumSamples (wpc)) {
|
||||
fputs ("incorrect number of samples!\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WavpackGetNumErrors (wpc)) {
|
||||
fputs ("crc errors detected!\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t read_bytes (void *buff, int32_t bcount)
|
||||
{
|
||||
return fread (buff, 1, bcount, stdin);
|
||||
}
|
||||
|
||||
// Reformat samples from longs in processor's native endian mode to
|
||||
// little-endian data with (possibly) less than 4 bytes / sample.
|
||||
|
||||
static uchar *format_samples (int bps, uchar *dst, int32_t *src, uint32_t samcnt)
|
||||
{
|
||||
int32_t temp;
|
||||
|
||||
switch (bps) {
|
||||
|
||||
case 1:
|
||||
while (samcnt--)
|
||||
*dst++ = *src++ + 128;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
while (samcnt--) {
|
||||
*dst++ = (uchar)(temp = *src++);
|
||||
*dst++ = (uchar)(temp >> 8);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
while (samcnt--) {
|
||||
*dst++ = (uchar)(temp = *src++);
|
||||
*dst++ = (uchar)(temp >> 8);
|
||||
*dst++ = (uchar)(temp >> 16);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
while (samcnt--) {
|
||||
*dst++ = (uchar)(temp = *src++);
|
||||
*dst++ = (uchar)(temp >> 8);
|
||||
*dst++ = (uchar)(temp >> 16);
|
||||
*dst++ = (uchar)(temp >> 24);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/e_network.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_engine.h>
|
||||
|
||||
#include <mastersrv/mastersrv.h>
|
||||
|
||||
extern NETSERVER *net;
|
||||
|
||||
enum
|
||||
{
|
||||
REGISTERSTATE_START=0,
|
||||
REGISTERSTATE_UPDATE_ADDRS,
|
||||
REGISTERSTATE_QUERY_COUNT,
|
||||
REGISTERSTATE_HEARTBEAT,
|
||||
REGISTERSTATE_REGISTERED,
|
||||
REGISTERSTATE_ERROR
|
||||
};
|
||||
|
||||
static int register_state = REGISTERSTATE_START;
|
||||
static int64 register_state_start = 0;
|
||||
static int register_first = 1;
|
||||
static int register_count = 0;
|
||||
|
||||
static void register_new_state(int state)
|
||||
{
|
||||
register_state = state;
|
||||
register_state_start = time_get();
|
||||
}
|
||||
|
||||
static void register_send_fwcheckresponse(NETADDR *addr)
|
||||
{
|
||||
NETCHUNK packet;
|
||||
packet.client_id = -1;
|
||||
packet.address = *addr;
|
||||
packet.flags = NETSENDFLAG_CONNLESS;
|
||||
packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
|
||||
packet.data = SERVERBROWSE_FWRESPONSE;
|
||||
netserver_send(net, &packet);
|
||||
}
|
||||
|
||||
static void register_send_heartbeat(NETADDR addr)
|
||||
{
|
||||
static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
|
||||
unsigned short port = config.sv_port;
|
||||
NETCHUNK packet;
|
||||
|
||||
mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
|
||||
|
||||
packet.client_id = -1;
|
||||
packet.address = addr;
|
||||
packet.flags = NETSENDFLAG_CONNLESS;
|
||||
packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
|
||||
packet.data = &data;
|
||||
|
||||
/* supply the set port that the master can use if it has problems */
|
||||
if(config.sv_external_port)
|
||||
port = config.sv_external_port;
|
||||
data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8;
|
||||
data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff;
|
||||
netserver_send(net, &packet);
|
||||
}
|
||||
|
||||
static void register_send_count_request(NETADDR addr)
|
||||
{
|
||||
NETCHUNK packet;
|
||||
packet.client_id = -1;
|
||||
packet.address = addr;
|
||||
packet.flags = NETSENDFLAG_CONNLESS;
|
||||
packet.data_size = sizeof(SERVERBROWSE_GETCOUNT);
|
||||
packet.data = SERVERBROWSE_GETCOUNT;
|
||||
netserver_send(net, &packet);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NETADDR addr;
|
||||
int count;
|
||||
int valid;
|
||||
int64 last_send;
|
||||
} MASTERSERVER_INFO;
|
||||
|
||||
static MASTERSERVER_INFO masterserver_info[MAX_MASTERSERVERS] = {{{0}}};
|
||||
static int register_registered_server = -1;
|
||||
|
||||
void register_update()
|
||||
{
|
||||
int64 now = time_get();
|
||||
int64 freq = time_freq();
|
||||
|
||||
if(!config.sv_register)
|
||||
return;
|
||||
|
||||
mastersrv_update();
|
||||
|
||||
if(register_state == REGISTERSTATE_START)
|
||||
{
|
||||
register_count = 0;
|
||||
register_first = 1;
|
||||
register_new_state(REGISTERSTATE_UPDATE_ADDRS);
|
||||
mastersrv_refresh_addresses();
|
||||
dbg_msg("register", "refreshing ip addresses");
|
||||
}
|
||||
else if(register_state == REGISTERSTATE_UPDATE_ADDRS)
|
||||
{
|
||||
register_registered_server = -1;
|
||||
|
||||
if(!mastersrv_refreshing())
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
NETADDR addr = mastersrv_get(i);
|
||||
masterserver_info[i].addr = addr;
|
||||
masterserver_info[i].count = 0;
|
||||
|
||||
if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3])
|
||||
masterserver_info[i].valid = 0;
|
||||
else
|
||||
{
|
||||
masterserver_info[i].valid = 1;
|
||||
masterserver_info[i].count = -1;
|
||||
masterserver_info[i].last_send = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dbg_msg("register", "fetching server counts");
|
||||
register_new_state(REGISTERSTATE_QUERY_COUNT);
|
||||
}
|
||||
}
|
||||
else if(register_state == REGISTERSTATE_QUERY_COUNT)
|
||||
{
|
||||
int i;
|
||||
int left = 0;
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
if(!masterserver_info[i].valid)
|
||||
continue;
|
||||
|
||||
if(masterserver_info[i].count == -1)
|
||||
{
|
||||
left++;
|
||||
if(masterserver_info[i].last_send+freq < now)
|
||||
{
|
||||
masterserver_info[i].last_send = now;
|
||||
register_send_count_request(masterserver_info[i].addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if we are done or timed out */
|
||||
if(left == 0 || now > register_state_start+freq*3)
|
||||
{
|
||||
/* choose server */
|
||||
int best = -1;
|
||||
int i;
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
if(!masterserver_info[i].valid || masterserver_info[i].count == -1)
|
||||
continue;
|
||||
|
||||
if(best == -1 || masterserver_info[i].count < masterserver_info[best].count)
|
||||
best = i;
|
||||
}
|
||||
|
||||
/* server chosen */
|
||||
register_registered_server = best;
|
||||
if(register_registered_server == -1)
|
||||
{
|
||||
dbg_msg("register", "WARNING: No master servers. Retrying in 60 seconds");
|
||||
register_new_state(REGISTERSTATE_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("register", "choosen '%s' as master, sending heartbeats", mastersrv_name(register_registered_server));
|
||||
masterserver_info[register_registered_server].last_send = 0;
|
||||
register_new_state(REGISTERSTATE_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(register_state == REGISTERSTATE_HEARTBEAT)
|
||||
{
|
||||
/* check if we should send heartbeat */
|
||||
if(now > masterserver_info[register_registered_server].last_send+freq*15)
|
||||
{
|
||||
masterserver_info[register_registered_server].last_send = now;
|
||||
register_send_heartbeat(masterserver_info[register_registered_server].addr);
|
||||
}
|
||||
|
||||
if(now > register_state_start+freq*60)
|
||||
{
|
||||
dbg_msg("register", "WARNING: Master server is not responding, switching master");
|
||||
register_new_state(REGISTERSTATE_START);
|
||||
}
|
||||
}
|
||||
else if(register_state == REGISTERSTATE_REGISTERED)
|
||||
{
|
||||
if(register_first)
|
||||
dbg_msg("register", "server registered");
|
||||
|
||||
register_first = 0;
|
||||
|
||||
/* check if we should send new heartbeat again */
|
||||
if(now > register_state_start+freq)
|
||||
{
|
||||
if(register_count == 120) /* redo the whole process after 60 minutes to balance out the master servers */
|
||||
register_new_state(REGISTERSTATE_START);
|
||||
else
|
||||
{
|
||||
register_count++;
|
||||
register_new_state(REGISTERSTATE_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(register_state == REGISTERSTATE_ERROR)
|
||||
{
|
||||
/* check for restart */
|
||||
if(now > register_state_start+freq*60)
|
||||
register_new_state(REGISTERSTATE_START);
|
||||
}
|
||||
}
|
||||
|
||||
static void register_got_count(NETCHUNK *p)
|
||||
{
|
||||
unsigned char *data = (unsigned char *)p->data;
|
||||
int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1];
|
||||
int i;
|
||||
|
||||
for(i = 0; i < MAX_MASTERSERVERS; i++)
|
||||
{
|
||||
if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0)
|
||||
{
|
||||
masterserver_info[i].count = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int register_process_packet(NETCHUNK *packet)
|
||||
{
|
||||
if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) &&
|
||||
memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
|
||||
{
|
||||
register_send_fwcheckresponse(&packet->address);
|
||||
return 1;
|
||||
}
|
||||
else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) &&
|
||||
memcmp(packet->data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
|
||||
{
|
||||
if(register_first)
|
||||
dbg_msg("register", "no firewall/nat problems detected");
|
||||
register_new_state(REGISTERSTATE_REGISTERED);
|
||||
return 1;
|
||||
}
|
||||
else if(packet->data_size == sizeof(SERVERBROWSE_FWERROR) &&
|
||||
memcmp(packet->data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
|
||||
{
|
||||
dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server.");
|
||||
dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port);
|
||||
register_new_state(REGISTERSTATE_ERROR);
|
||||
return 1;
|
||||
}
|
||||
else if(packet->data_size == sizeof(SERVERBROWSE_COUNT)+2 &&
|
||||
memcmp(packet->data, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0)
|
||||
{
|
||||
register_got_count(packet);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +0,0 @@
|
||||
|
||||
#include <base/math.hpp>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include "animstate.hpp"
|
||||
|
||||
static void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame)
|
||||
{
|
||||
if(seq->num_frames == 0)
|
||||
{
|
||||
frame->time = 0;
|
||||
frame->x = 0;
|
||||
frame->y = 0;
|
||||
frame->angle = 0;
|
||||
}
|
||||
else if(seq->num_frames == 1)
|
||||
{
|
||||
*frame = seq->frames[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
//time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp
|
||||
ANIM_KEYFRAME *frame1 = 0;
|
||||
ANIM_KEYFRAME *frame2 = 0;
|
||||
float blend = 0.0f;
|
||||
|
||||
// TODO: make this smarter.. binary search
|
||||
for (int i = 1; i < seq->num_frames; i++)
|
||||
{
|
||||
if (seq->frames[i-1].time <= time && seq->frames[i].time >= time)
|
||||
{
|
||||
frame1 = &seq->frames[i-1];
|
||||
frame2 = &seq->frames[i];
|
||||
blend = (time - frame1->time) / (frame2->time - frame1->time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame1 && frame2)
|
||||
{
|
||||
frame->time = time;
|
||||
frame->x = mix(frame1->x, frame2->x, blend);
|
||||
frame->y = mix(frame1->y, frame2->y, blend);
|
||||
frame->angle = mix(frame1->angle, frame2->angle, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount)
|
||||
{
|
||||
seq->x += added->x*amount;
|
||||
seq->y += added->y*amount;
|
||||
seq->angle += added->angle*amount;
|
||||
}
|
||||
|
||||
static void anim_add(ANIMSTATE *state, ANIMSTATE *added, float amount)
|
||||
{
|
||||
anim_add_keyframe(&state->body, &added->body, amount);
|
||||
anim_add_keyframe(&state->back_foot, &added->back_foot, amount);
|
||||
anim_add_keyframe(&state->front_foot, &added->front_foot, amount);
|
||||
anim_add_keyframe(&state->attach, &added->attach, amount);
|
||||
}
|
||||
|
||||
|
||||
void ANIMSTATE::set(ANIMATION *anim, float time)
|
||||
{
|
||||
anim_seq_eval(&anim->body, time, &body);
|
||||
anim_seq_eval(&anim->back_foot, time, &back_foot);
|
||||
anim_seq_eval(&anim->front_foot, time, &front_foot);
|
||||
anim_seq_eval(&anim->attach, time, &attach);
|
||||
}
|
||||
|
||||
void ANIMSTATE::add(ANIMATION *anim, float time, float amount)
|
||||
{
|
||||
ANIMSTATE add;
|
||||
add.set(anim, time);
|
||||
anim_add(this, &add, amount);
|
||||
}
|
||||
|
||||
ANIMSTATE *ANIMSTATE::get_idle()
|
||||
{
|
||||
static ANIMSTATE state;
|
||||
static bool init = true;
|
||||
|
||||
if(init)
|
||||
{
|
||||
state.set(&data->animations[ANIM_BASE], 0);
|
||||
state.add(&data->animations[ANIM_IDLE], 0, 1.0f);
|
||||
init = false;
|
||||
}
|
||||
|
||||
return &state;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef GAME_CLIENT_ANIMATION_H
|
||||
#define GAME_CLIENT_ANIMATION_H
|
||||
|
||||
class ANIMSTATE
|
||||
{
|
||||
public:
|
||||
ANIM_KEYFRAME body;
|
||||
ANIM_KEYFRAME back_foot;
|
||||
ANIM_KEYFRAME front_foot;
|
||||
ANIM_KEYFRAME attach;
|
||||
|
||||
void set(ANIMATION *anim, float time);
|
||||
void add(ANIMATION *added, float time, float amount);
|
||||
|
||||
static ANIMSTATE *get_idle();
|
||||
};
|
||||
|
||||
//void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame);
|
||||
//void anim_eval(ANIMATION *anim, float time, ANIM_STATE *state);
|
||||
//void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount);
|
||||
//void anim_add(ANIM_STATE *state, ANIM_STATE *added, float amount);
|
||||
//void anim_eval_add(ANIM_STATE *state, ANIMATION *anim, float time, float amount);
|
||||
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/version.hpp>
|
||||
|
||||
#include "gameclient.hpp"
|
||||
#include "components/console.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
// clean hooks
|
||||
extern "C" void modc_entergame() {}
|
||||
extern "C" void modc_shutdown() {}
|
||||
extern "C" void modc_console_init() { gameclient.on_console_init(); }
|
||||
extern "C" void modc_save_config() { gameclient.on_save(); }
|
||||
extern "C" void modc_init() { gameclient.on_init(); }
|
||||
extern "C" void modc_connected() { gameclient.on_connected(); }
|
||||
extern "C" void modc_predict() { gameclient.on_predict(); }
|
||||
extern "C" void modc_newsnapshot() { gameclient.on_snapshot(); }
|
||||
extern "C" int modc_snap_input(int *data) { return gameclient.on_snapinput(data); }
|
||||
extern "C" void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); }
|
||||
extern "C" void modc_render() { gameclient.on_render(); }
|
||||
extern "C" void modc_message(int msgtype) { gameclient.on_message(msgtype); }
|
||||
extern "C" void modc_rcon_line(const char *line) { gameclient.console->print_line(1, line); }
|
||||
|
||||
extern "C" const char *modc_net_version() { return GAME_NETVERSION; }
|
||||
extern "C" const char *modc_getitemname(int type) { return netobj_get_name(type); }
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef GAME_CLIENT_GAMESYSTEM_H
|
||||
#define GAME_CLIENT_GAMESYSTEM_H
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
class GAMECLIENT;
|
||||
|
||||
class COMPONENT
|
||||
{
|
||||
protected:
|
||||
GAMECLIENT *client;
|
||||
public:
|
||||
virtual ~COMPONENT() {}
|
||||
|
||||
virtual void on_statechange(int new_state, int old_state) {};
|
||||
virtual void on_console_init() {};
|
||||
virtual void on_init() {};
|
||||
virtual void on_save() {};
|
||||
virtual void on_reset() {};
|
||||
virtual void on_render() {};
|
||||
virtual void on_mapload() {};
|
||||
virtual void on_message(int msg, void *rawmsg) {}
|
||||
virtual bool on_mousemove(float x, float y) { return false; }
|
||||
virtual bool on_input(INPUT_EVENT e) { return false; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,241 +0,0 @@
|
||||
#include <stdlib.h> // atoi
|
||||
#include <string.h> // strcmp
|
||||
#include <engine/e_client_interface.h>
|
||||
#include "binds.hpp"
|
||||
|
||||
bool BINDS::BINDS_SPECIAL::on_input(INPUT_EVENT e)
|
||||
{
|
||||
// don't handle invalid events and keys that arn't set to anything
|
||||
if(e.key >= KEY_F1 && e.key <= KEY_F15 && binds->keybindings[e.key][0] != 0)
|
||||
{
|
||||
int stroke = 0;
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
stroke = 1;
|
||||
console_execute_line_stroked(stroke, binds->keybindings[e.key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BINDS::BINDS()
|
||||
{
|
||||
mem_zero(keybindings, sizeof(keybindings));
|
||||
special_binds.binds = this;
|
||||
}
|
||||
|
||||
void BINDS::bind(int keyid, const char *str)
|
||||
{
|
||||
if(keyid < 0 || keyid >= KEY_LAST)
|
||||
return;
|
||||
|
||||
str_copy(keybindings[keyid], str, sizeof(keybindings[keyid]));
|
||||
if(!keybindings[keyid][0])
|
||||
dbg_msg("binds", "unbound %s (%d)", inp_key_name(keyid), keyid);
|
||||
else
|
||||
dbg_msg("binds", "bound %s (%d) = %s", inp_key_name(keyid), keyid, keybindings[keyid]);
|
||||
}
|
||||
|
||||
|
||||
bool BINDS::on_input(INPUT_EVENT e)
|
||||
{
|
||||
// don't handle invalid events and keys that arn't set to anything
|
||||
if(e.key <= 0 || e.key >= KEY_LAST || keybindings[e.key][0] == 0)
|
||||
return false;
|
||||
|
||||
int stroke = 0;
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
stroke = 1;
|
||||
console_execute_line_stroked(stroke, keybindings[e.key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BINDS::unbindall()
|
||||
{
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
keybindings[i][0] = 0;
|
||||
}
|
||||
|
||||
const char *BINDS::get(int keyid)
|
||||
{
|
||||
if(keyid > 0 && keyid < KEY_LAST)
|
||||
return keybindings[keyid];
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *BINDS::get_key(const char *bindstr)
|
||||
{
|
||||
for(int keyid = 0; keyid < KEY_LAST; keyid++)
|
||||
{
|
||||
const char *bind = get(keyid);
|
||||
if(!bind[0])
|
||||
continue;
|
||||
|
||||
if(strcmp(bind, bindstr) == 0)
|
||||
return inp_key_name(keyid);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void BINDS::set_defaults()
|
||||
{
|
||||
// set default key bindings
|
||||
unbindall();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
bind(KEY_MOUSE_1, "+fire");
|
||||
bind(KEY_LEFT, "+left");
|
||||
bind(KEY_RIGHT, "+right");
|
||||
bind(KEY_UP, "+jump");
|
||||
bind(KEY_DOWN, "+hook");
|
||||
bind(KEY_RETURN, "+jump");
|
||||
bind(KEY_SPACE, "+hook");
|
||||
bind(KEY_MOUSE_2, "+hook");
|
||||
bind(KEY_LSHIFT, "+nextweapon");
|
||||
bind(KEY_RSHIFT, "+prevweapon");
|
||||
|
||||
#else
|
||||
|
||||
bind(KEY_F1, "toggle_local_console");
|
||||
bind(KEY_F2, "toggle_remote_console");
|
||||
bind(KEY_TAB, "+scoreboard");
|
||||
bind(KEY_F10, "screenshot");
|
||||
|
||||
bind('a', "+left");
|
||||
bind('d', "+right");
|
||||
|
||||
bind(KEY_SPACE, "+jump");
|
||||
bind(KEY_MOUSE_1, "+fire");
|
||||
bind(KEY_MOUSE_2, "+hook");
|
||||
bind(KEY_LSHIFT, "+emote");
|
||||
|
||||
bind('1', "+weapon1");
|
||||
bind('2', "+weapon2");
|
||||
bind('3', "+weapon3");
|
||||
bind('4', "+weapon4");
|
||||
bind('5', "+weapon5");
|
||||
|
||||
bind(KEY_MOUSE_WHEEL_UP, "+prevweapon");
|
||||
bind(KEY_MOUSE_WHEEL_DOWN, "+nextweapon");
|
||||
|
||||
bind('t', "chat all");
|
||||
bind('y', "chat team");
|
||||
|
||||
bind(KEY_F3, "vote yes");
|
||||
bind(KEY_F4, "vote no");
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void BINDS::on_console_init()
|
||||
{
|
||||
// bindings
|
||||
MACRO_REGISTER_COMMAND("bind", "sr", CFGFLAG_CLIENT, con_bind, this, "Bind key to execute the command");
|
||||
MACRO_REGISTER_COMMAND("unbind", "s", CFGFLAG_CLIENT, con_unbind, this, "Unbind key");
|
||||
MACRO_REGISTER_COMMAND("unbindall", "", CFGFLAG_CLIENT, con_unbindall, this, "Unbind all keys");
|
||||
MACRO_REGISTER_COMMAND("dump_binds", "", CFGFLAG_CLIENT, con_dump_binds, this, "Dump binds");
|
||||
|
||||
// default bindings
|
||||
set_defaults();
|
||||
}
|
||||
|
||||
void BINDS::con_bind(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
const char *key_name = console_arg_string(result, 0);
|
||||
int id = binds->get_key_id(key_name);
|
||||
|
||||
if(!id)
|
||||
{
|
||||
dbg_msg("binds", "key %s not found", key_name);
|
||||
return;
|
||||
}
|
||||
|
||||
binds->bind(id, console_arg_string(result, 1));
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_unbind(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
const char *key_name = console_arg_string(result, 0);
|
||||
int id = binds->get_key_id(key_name);
|
||||
|
||||
if(!id)
|
||||
{
|
||||
dbg_msg("binds", "key %s not found", key_name);
|
||||
return;
|
||||
}
|
||||
|
||||
binds->bind(id, "");
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_unbindall(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
binds->unbindall();
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_dump_binds(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(binds->keybindings[i][0] == 0)
|
||||
continue;
|
||||
dbg_msg("binds", "%s (%d) = %s", inp_key_name(i), i, binds->keybindings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int BINDS::get_key_id(const char *key_name)
|
||||
{
|
||||
// check for numeric
|
||||
if(key_name[0] == '&')
|
||||
{
|
||||
int i = atoi(key_name+1);
|
||||
if(i > 0 && i < KEY_LAST)
|
||||
return i; // numeric
|
||||
}
|
||||
|
||||
// search for key
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(strcmp(key_name, inp_key_name(i)) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BINDS::on_save()
|
||||
{
|
||||
char buffer[256];
|
||||
char *end = buffer+sizeof(buffer)-8;
|
||||
client_save_line("unbindall");
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(keybindings[i][0] == 0)
|
||||
continue;
|
||||
str_format(buffer, sizeof(buffer), "bind %s ", inp_key_name(i));
|
||||
|
||||
// process the string. we need to escape some characters
|
||||
const char *src = keybindings[i];
|
||||
char *dst = buffer + strlen(buffer);
|
||||
*dst++ = '"';
|
||||
while(*src && dst < end)
|
||||
{
|
||||
if(*src == '"' || *src == '\\') // escape \ and "
|
||||
*dst++ = '\\';
|
||||
*dst++ = *src++;
|
||||
}
|
||||
*dst++ = '"';
|
||||
*dst++ = 0;
|
||||
|
||||
client_save_line(buffer);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class BINDS : public COMPONENT
|
||||
{
|
||||
char keybindings[KEY_LAST][128];
|
||||
|
||||
int get_key_id(const char *key_name);
|
||||
|
||||
static void con_bind(void *result, void *user_data);
|
||||
static void con_unbind(void *result, void *user_data);
|
||||
static void con_unbindall(void *result, void *user_data);
|
||||
static void con_dump_binds(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
BINDS();
|
||||
|
||||
class BINDS_SPECIAL : public COMPONENT
|
||||
{
|
||||
public:
|
||||
BINDS *binds;
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
|
||||
BINDS_SPECIAL special_binds;
|
||||
|
||||
void bind(int keyid, const char *str);
|
||||
void set_defaults();
|
||||
void unbindall();
|
||||
const char *get(int keyid);
|
||||
const char *get_key(const char *bindstr);
|
||||
|
||||
virtual void on_save();
|
||||
virtual void on_console_init();
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
#include "broadcast.hpp"
|
||||
|
||||
void BROADCAST::on_reset()
|
||||
{
|
||||
broadcast_time = 0;
|
||||
}
|
||||
|
||||
void BROADCAST::on_render()
|
||||
{
|
||||
gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
|
||||
|
||||
if(time_get() < broadcast_time)
|
||||
{
|
||||
float w = gfx_text_width(0, 14, broadcast_text, -1);
|
||||
gfx_text(0, 150*gfx_screenaspect()-w/2, 35, 14, broadcast_text, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void BROADCAST::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_BROADCAST)
|
||||
{
|
||||
NETMSG_SV_BROADCAST *msg = (NETMSG_SV_BROADCAST *)rawmsg;
|
||||
str_copy(broadcast_text, msg->message, sizeof(broadcast_text));
|
||||
broadcast_time = time_get()+time_freq()*10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class BROADCAST : public COMPONENT
|
||||
{
|
||||
public:
|
||||
// broadcasts
|
||||
char broadcast_text[1024];
|
||||
int64 broadcast_time;
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
};
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
extern "C" {
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
}
|
||||
|
||||
#include <base/math.hpp>
|
||||
#include <game/collision.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
#include "camera.hpp"
|
||||
#include "controls.hpp"
|
||||
|
||||
CAMERA::CAMERA()
|
||||
{
|
||||
}
|
||||
|
||||
void CAMERA::on_render()
|
||||
{
|
||||
//vec2 center;
|
||||
zoom = 1.0f;
|
||||
|
||||
// update camera center
|
||||
if(gameclient.snap.spectate)
|
||||
center = gameclient.controls->mouse_pos;
|
||||
else
|
||||
{
|
||||
|
||||
float l = length(gameclient.controls->mouse_pos);
|
||||
float deadzone = config.cl_mouse_deadzone;
|
||||
float follow_factor = config.cl_mouse_followfactor/100.0f;
|
||||
vec2 camera_offset(0, 0);
|
||||
|
||||
float offset_amount = max(l-deadzone, 0.0f) * follow_factor;
|
||||
if(l > 0.0001f) // make sure that this isn't 0
|
||||
camera_offset = normalize(gameclient.controls->mouse_pos)*offset_amount;
|
||||
|
||||
center = gameclient.local_character_pos + camera_offset;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class CAMERA : public COMPONENT
|
||||
{
|
||||
public:
|
||||
vec2 center;
|
||||
float zoom;
|
||||
|
||||
CAMERA();
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user