Removed old TeeWorlds repo

This commit is contained in:
Sergii Pylypenko
2013-06-11 17:10:50 +03:00
parent 2dd6ca7d2e
commit 0af04ec0fe
215 changed files with 3 additions and 49418 deletions

View File

@@ -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.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();
> }
> }

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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_);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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__ */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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; }

View File

@@ -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

View File

@@ -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++;
}
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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; }

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 &empty;
}
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);
}

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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.";
};
}

View File

@@ -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

View File

@@ -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}

View File

@@ -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}

View File

@@ -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++;
}
}

View File

@@ -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

View File

@@ -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++;
}
}

View File

@@ -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.

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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); }

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
};

View File

@@ -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;
}
}

View File

@@ -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);
};

View File

@@ -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;
}
}

View File

@@ -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