demo-reader 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/README.rdoc +1 -1
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/demo-reader.gemspec +30 -7
- data/ext/dm68/README.txt +14 -0
- data/ext/dm68/common.c +797 -0
- data/ext/dm68/common.h +568 -0
- data/ext/dm68/dm68.c +57 -0
- data/ext/dm68/dump.c +196 -0
- data/ext/dm68/extconf.rb +16 -0
- data/ext/dm68/huff.c +542 -0
- data/ext/dm68/huff.h +34 -0
- data/ext/dm68/main.c +175 -0
- data/ext/dm68/main.h +126 -0
- data/ext/dm68/msg.c +1082 -0
- data/ext/dm68/msg.h +114 -0
- data/ext/dm68/parse.c +393 -0
- data/lib/demo-reader.rb +11 -3
- data/lib/demo_reader_defrag.rb +106 -0
- data/lib/demo_reader_warsow.rb +3 -5
- data/test/demo_reader_defrag_dm_68_cpm_test.rb +29 -0
- data/test/demo_reader_defrag_dm_68_vq3_test.rb +29 -0
- data/test/fixtures/defrag/dm_68/cpm/pornchronostar_mdf.cpm_00.49.216_tyaz.germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/cpm/puremotion_df.cpm_00.10.600_eS-Rody.russia.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/runkull2_df.vq3_01.05.904_XunderBIRD.Germany.dm_68 +0 -0
- data/test/fixtures/defrag/dm_68/vq3/un-dead029_df.vq3_00.16.912_uN-DeaD!WiNTeR.ru.dm_68 +0 -0
- metadata +43 -9
data/ext/dm68/dm68.c
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "main.h"
|
3
|
+
|
4
|
+
|
5
|
+
static VALUE rb_mDM68;
|
6
|
+
static VALUE rb_cDM68;
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
static VALUE parse_file(VALUE obj, VALUE path)
|
12
|
+
{
|
13
|
+
Check_Type(path, T_STRING);
|
14
|
+
|
15
|
+
// reset global state
|
16
|
+
memset(result, 0, MAXRESULT);
|
17
|
+
demo.demoMessageSequence = 0;
|
18
|
+
demo.gameStatesParsed = 0;
|
19
|
+
|
20
|
+
if(!(demo.demofile=fopen(RSTRING(path)->ptr, "rb")))
|
21
|
+
{
|
22
|
+
Com_Error(ERR_FATAL, "Couldn't open demofile");
|
23
|
+
}
|
24
|
+
|
25
|
+
Huff_Init();
|
26
|
+
|
27
|
+
append_result("---\n");
|
28
|
+
|
29
|
+
while(!demo.gameStatesParsed) {
|
30
|
+
if(!Parse_NextDemoMessage()) {
|
31
|
+
break;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
GameStateParsed();
|
36
|
+
|
37
|
+
append_result("prints:\n");
|
38
|
+
|
39
|
+
while(1) {
|
40
|
+
if(!Parse_NextDemoMessage()) {
|
41
|
+
break;
|
42
|
+
}
|
43
|
+
NewFrameParsed();
|
44
|
+
}
|
45
|
+
|
46
|
+
fclose(demo.demofile);
|
47
|
+
|
48
|
+
return rb_str_new2(result);
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
void Init_dm68()
|
53
|
+
{
|
54
|
+
rb_mDM68 = rb_define_module("DM68");
|
55
|
+
rb_define_module_function(rb_mDM68, "parse_file", parse_file, 1);
|
56
|
+
}
|
57
|
+
|
data/ext/dm68/dump.c
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
//
|
2
|
+
// dump.c - ugly stuff, don't look!
|
3
|
+
//
|
4
|
+
|
5
|
+
#include "main.h"
|
6
|
+
|
7
|
+
typedef struct {
|
8
|
+
entityState_t currentState; // zeiger auf ringbuff pso
|
9
|
+
qboolean currentValid; // true if cg.frame holds this entity
|
10
|
+
int previousEvent;
|
11
|
+
} centity_t;
|
12
|
+
|
13
|
+
static char clientNames[MAX_CLIENTS][36];
|
14
|
+
static centity_t centities[MAX_GENTITIES];
|
15
|
+
|
16
|
+
static void UpdateClientInfo( int clientNum, const char *info ) {
|
17
|
+
if( !info || !info[0] ) {
|
18
|
+
Q_strncpyz( clientNames[clientNum], "BADNAME", sizeof( clientNames[0] ) );
|
19
|
+
} else {
|
20
|
+
Q_strncpyz( clientNames[clientNum], Info_ValueForKey( info, "n" ), sizeof( clientNames[0] ) );
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
static void CG_CheckEvents( centity_t *cent ) {
|
25
|
+
entityState_t *es = ¢->currentState;
|
26
|
+
int event;
|
27
|
+
int mod;
|
28
|
+
int target, attacker;
|
29
|
+
char *targetName, *attackerName;
|
30
|
+
char *message;
|
31
|
+
char *message2 = "";
|
32
|
+
|
33
|
+
// check for event-only entities
|
34
|
+
if( es->eType > ET_EVENTS ) {
|
35
|
+
if( cent->previousEvent ) {
|
36
|
+
return; // already fired
|
37
|
+
}
|
38
|
+
cent->previousEvent = 1;
|
39
|
+
|
40
|
+
es->event = es->eType - ET_EVENTS;
|
41
|
+
} else {
|
42
|
+
// check for events riding with another entity
|
43
|
+
if ( es->event == cent->previousEvent ) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
cent->previousEvent = es->event;
|
47
|
+
if( ( es->event & ~EV_EVENT_BITS ) == 0 ) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
event = es->event & ~EV_EVENT_BITS;
|
53
|
+
if( event != EV_OBITUARY ) {
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
|
57
|
+
target = es->otherEntityNum;
|
58
|
+
attacker = es->otherEntityNum2;
|
59
|
+
mod = es->eventParm;
|
60
|
+
|
61
|
+
if( target < 0 || target >= MAX_CLIENTS ) {
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
|
65
|
+
if( attacker < 0 || attacker >= MAX_CLIENTS ) {
|
66
|
+
attacker = target;
|
67
|
+
}
|
68
|
+
|
69
|
+
targetName = clientNames[target];
|
70
|
+
attackerName = clientNames[attacker];
|
71
|
+
|
72
|
+
if (attacker == target) // selbstmord oder "unfall"
|
73
|
+
{
|
74
|
+
switch (mod)
|
75
|
+
{
|
76
|
+
case MOD_GRENADE_SPLASH:message = "tripped on own grenade."; break;
|
77
|
+
case MOD_ROCKET_SPLASH: message = "blew up by own rocket.";break;
|
78
|
+
case MOD_PLASMA_SPLASH: message = "melted by own plasma."; break;
|
79
|
+
case MOD_BFG_SPLASH: message = "should have used a smaller gun."; break;
|
80
|
+
case MOD_SUICIDE: message = "suicides";break;
|
81
|
+
case MOD_FALLING: message = "cratered";break;
|
82
|
+
case MOD_CRUSH: message = "was squished";break;
|
83
|
+
case MOD_WATER: message = "sank like a rock";break;
|
84
|
+
case MOD_SLIME: message = "melted"; break;
|
85
|
+
case MOD_LAVA: message = "does a back flip into the lava"; break;
|
86
|
+
case MOD_TARGET_LASER: message = "saw the light";break;
|
87
|
+
case MOD_TRIGGER_HURT: message = "was in the wrong place"; break;
|
88
|
+
default: message = "committed suicid."; break;
|
89
|
+
}
|
90
|
+
Com_Printf( "%s %s.\n", targetName, message );
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
|
94
|
+
switch (mod)
|
95
|
+
{
|
96
|
+
case MOD_GRAPPLE: message = "was caught by"; break;
|
97
|
+
case MOD_GAUNTLET: message = "was pummeled by";break;
|
98
|
+
case MOD_MACHINEGUN: message = "was machinegunned by";break;
|
99
|
+
case MOD_SHOTGUN: message = "was gunned down by"; break;
|
100
|
+
case MOD_GRENADE: message = "ate";message2 = "'s grenade";break;
|
101
|
+
case MOD_GRENADE_SPLASH:message = "was shredded by";message2 = "'s shrapnel";break;
|
102
|
+
case MOD_ROCKET: message = "ate";message2 = "'s rocket";break;
|
103
|
+
case MOD_ROCKET_SPLASH: message = "almost dodged";message2 = "'s rocket";break;
|
104
|
+
case MOD_PLASMA: message = "was melted by";message2 = "'s plasmagun";break;
|
105
|
+
case MOD_PLASMA_SPLASH: message = "was melted by";message2 = "'s plasmagun";break;
|
106
|
+
case MOD_RAILGUN: message = "was railed by";break;
|
107
|
+
case MOD_LIGHTNING: message = "was electrocuted by";break;
|
108
|
+
case MOD_BFG:
|
109
|
+
case MOD_BFG_SPLASH: message = "was blasted by";message2 = "'s BFG"; break;
|
110
|
+
case MOD_TELEFRAG: message = "tried to invade";message2 = "'s personal space"; break;
|
111
|
+
default: message = "was killed by";break;
|
112
|
+
}
|
113
|
+
Com_Printf( "%s %s %s%s.\n", targetName, message, attackerName, message2);
|
114
|
+
return;
|
115
|
+
|
116
|
+
|
117
|
+
// we don't know what it was
|
118
|
+
Com_Printf( "%s died.\n", targetName );
|
119
|
+
}
|
120
|
+
|
121
|
+
void GameStateParsed( void ) {
|
122
|
+
int i;
|
123
|
+
char *configString;
|
124
|
+
|
125
|
+
for( i=0 ; i<MAX_CONFIGSTRINGS ; i++ ) {
|
126
|
+
configString = Com_GetStringFromGameState( &ds.gameState, i );
|
127
|
+
if( configString[0] ) {
|
128
|
+
if( i < RESERVED_CONFIGSTRINGS ) {
|
129
|
+
append_result( "%s_info:\n", (i == CS_SERVERINFO) ? "server" : "system" );
|
130
|
+
Info_Print( configString );
|
131
|
+
append_result( "\n" );
|
132
|
+
}/* else {
|
133
|
+
Com_Printf( "configString %i: \"%s\"\n", i, configString );
|
134
|
+
}*/
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
for( i=0 ; i<MAX_CLIENTS ; i++ ) {
|
139
|
+
configString = Com_GetStringFromGameState( &ds.gameState, CS_PLAYERS + i );
|
140
|
+
UpdateClientInfo( i, configString );
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
static void UpdateConfigString( int index, char *string ) {
|
145
|
+
Com_InsertIntoGameState( &ds.gameState, index, string );
|
146
|
+
|
147
|
+
if( index > CS_PLAYERS && index < CS_PLAYERS + MAX_CLIENTS ) {
|
148
|
+
UpdateClientInfo( index, string );
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
void NewFrameParsed( void ) {
|
153
|
+
int i;
|
154
|
+
entityState_t *es;
|
155
|
+
char *serverCommand;
|
156
|
+
char *args;
|
157
|
+
|
158
|
+
for( i=ds.currentServerCommandNum+1 ; i<=ds.lastServerCommandNum ; i++ ) {
|
159
|
+
serverCommand = ds.serverCommands[i & SERVERCMD_MASK];
|
160
|
+
Cmd_TokenizeString( serverCommand );
|
161
|
+
|
162
|
+
if( !Q_stricmp( Cmd_Argv( 0 ), "print" ) || !Q_stricmp( Cmd_Argv( 0 ), "cp" ) ) {
|
163
|
+
args = Cmd_Args();
|
164
|
+
args[strlen(args)-2] = 0;
|
165
|
+
args++;
|
166
|
+
|
167
|
+
append_result(" - \"%s\"\n", args);
|
168
|
+
} else if( !Q_stricmp( Cmd_Argv( 0 ), "cs" ) ) {
|
169
|
+
UpdateConfigString( atoi( Cmd_Argv( 1 ) ), Cmd_Argv( 2 ) );
|
170
|
+
}
|
171
|
+
|
172
|
+
// Com_Printf( "serverCommand: %i \"%s\"\n", i, Com_TranslateLinefeeds( ) );
|
173
|
+
}
|
174
|
+
ds.currentServerCommandNum = ds.lastServerCommandNum;
|
175
|
+
|
176
|
+
if( !ds.snap || !ds.snap->valid ) {
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
|
180
|
+
for( i=0 ; i<MAX_GENTITIES ; i++ ) {
|
181
|
+
centities[i].currentValid = qfalse;
|
182
|
+
}
|
183
|
+
|
184
|
+
for( i=0 ; i<ds.snap->numEntities ; i++ ) {
|
185
|
+
es = &ds.parseEntities[(ds.snap->firstEntity+i) & PARSE_ENTITIES_MASK];
|
186
|
+
memcpy( ¢ities[es->number].currentState, es, sizeof( entityState_t ) );
|
187
|
+
centities[es->number].currentValid = qtrue;
|
188
|
+
CG_CheckEvents( ¢ities[es->number] );
|
189
|
+
}
|
190
|
+
|
191
|
+
for( i=0 ; i<MAX_GENTITIES ; i++ ) {
|
192
|
+
if( !centities[i].currentValid ) {
|
193
|
+
centities[i].previousEvent = 0;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
data/ext/dm68/extconf.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
|
3
|
+
dir_config("dm68")
|
4
|
+
|
5
|
+
# have_library("common")
|
6
|
+
# have_library("huff")
|
7
|
+
# have_library("msg")
|
8
|
+
|
9
|
+
# have_library("dump")
|
10
|
+
# have_library("parse")
|
11
|
+
# have_library("main")
|
12
|
+
|
13
|
+
# $CFLAGS =
|
14
|
+
# $CFLAGS = "-Wall"
|
15
|
+
create_makefile("dm68")
|
16
|
+
|
data/ext/dm68/huff.c
ADDED
@@ -0,0 +1,542 @@
|
|
1
|
+
/*
|
2
|
+
Quake III *.dm_6? Demo Specifications
|
3
|
+
|
4
|
+
Copyright (C) 2003 Andrey '[SkulleR]' Nazarov
|
5
|
+
Based on Argus and Quake II source code
|
6
|
+
Also contains some stuff from Q3A SDK
|
7
|
+
|
8
|
+
Argus is Copyright (C) 2000 Martin Otten
|
9
|
+
Quake II and Quake III are Copyright (C) 1997-2001 ID Software, Inc
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or
|
12
|
+
modify it under the terms of the GNU General Public License
|
13
|
+
as published by the Free Software Foundation; either version 2
|
14
|
+
of the License, or (at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
19
|
+
|
20
|
+
See the GNU General Public License for more details.
|
21
|
+
|
22
|
+
You should have received a copy of the GNU General Public License
|
23
|
+
along with this program; if not, write to the Free Software
|
24
|
+
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
25
|
+
*/
|
26
|
+
|
27
|
+
//
|
28
|
+
// huff.c - Huffman compression routines for data bitstream
|
29
|
+
//
|
30
|
+
|
31
|
+
#include "common.h"
|
32
|
+
#include "huff.h"
|
33
|
+
|
34
|
+
#define VALUE(a) (*(int *)&(a))
|
35
|
+
#define NODE(a) ((void*)(a))
|
36
|
+
|
37
|
+
#define NODE_START NODE( 1)
|
38
|
+
#define NODE_NONE NODE(256)
|
39
|
+
#define NODE_NEXT NODE(257)
|
40
|
+
|
41
|
+
#define HUFF_TREE_SIZE 7175
|
42
|
+
typedef void *tree_t[HUFF_TREE_SIZE];
|
43
|
+
|
44
|
+
//
|
45
|
+
// pre-defined frequency counts for all bytes [0..255]
|
46
|
+
//
|
47
|
+
static int huffCounts[256] = {
|
48
|
+
0x3D1CB, 0x0A0E9, 0x01894, 0x01BC2, 0x00E92, 0x00EA6, 0x017DE, 0x05AF3,
|
49
|
+
0x08225, 0x01B26, 0x01E9E, 0x025F2, 0x02429, 0x0436B, 0x00F6D, 0x006F2,
|
50
|
+
0x02060, 0x00644, 0x00636, 0x0067F, 0x0044C, 0x004BD, 0x004D6, 0x0046E,
|
51
|
+
0x006D5, 0x00423, 0x004DE, 0x0047D, 0x004F9, 0x01186, 0x00AF5, 0x00D90,
|
52
|
+
0x0553B, 0x00487, 0x00686, 0x0042A, 0x00413, 0x003F4, 0x0041D, 0x0042E,
|
53
|
+
0x006BE, 0x00378, 0x0049C, 0x00352, 0x003C0, 0x0030C, 0x006D8, 0x00CE0,
|
54
|
+
0x02986, 0x011A2, 0x016F9, 0x00A7D, 0x0122A, 0x00EFD, 0x0082D, 0x0074B,
|
55
|
+
0x00A18, 0x0079D, 0x007B4, 0x003AC, 0x0046E, 0x006FC, 0x00686, 0x004B6,
|
56
|
+
0x01657, 0x017F0, 0x01C36, 0x019FE, 0x00E7E, 0x00ED3, 0x005D4, 0x005F4,
|
57
|
+
0x008A7, 0x00474, 0x0054B, 0x003CB, 0x00884, 0x004E0, 0x00530, 0x004AB,
|
58
|
+
0x006EA, 0x00436, 0x004F0, 0x004F2, 0x00490, 0x003C5, 0x00483, 0x004A2,
|
59
|
+
0x00543, 0x004CC, 0x005F9, 0x00640, 0x00A39, 0x00800, 0x009F2, 0x00CCB,
|
60
|
+
0x0096A, 0x00E01, 0x009C8, 0x00AF0, 0x00A73, 0x01802, 0x00E4F, 0x00B18,
|
61
|
+
0x037AD, 0x00C5C, 0x008AD, 0x00697, 0x00C88, 0x00AB3, 0x00DB8, 0x012BC,
|
62
|
+
0x00FFB, 0x00DBB, 0x014A8, 0x00FB0, 0x01F01, 0x0178F, 0x014F0, 0x00F54,
|
63
|
+
0x0131C, 0x00E9F, 0x011D6, 0x012C7, 0x016DC, 0x01900, 0x01851, 0x02063,
|
64
|
+
0x05ACB, 0x01E9E, 0x01BA1, 0x022E7, 0x0153D, 0x01183, 0x00E39, 0x01488,
|
65
|
+
0x014C0, 0x014D0, 0x014FA, 0x00DA4, 0x0099A, 0x0069E, 0x0071D, 0x00849,
|
66
|
+
0x0077C, 0x0047D, 0x005EC, 0x00557, 0x004D4, 0x00405, 0x004EA, 0x00450,
|
67
|
+
0x004DD, 0x003EE, 0x0047D, 0x00401, 0x004D9, 0x003B8, 0x00507, 0x003E5,
|
68
|
+
0x006B1, 0x003F1, 0x004A3, 0x0036F, 0x0044B, 0x003A1, 0x00436, 0x003B7,
|
69
|
+
0x00678, 0x003A2, 0x00481, 0x00406, 0x004EE, 0x00426, 0x004BE, 0x00424,
|
70
|
+
0x00655, 0x003A2, 0x00452, 0x00390, 0x0040A, 0x0037C, 0x00486, 0x003DE,
|
71
|
+
0x00497, 0x00352, 0x00461, 0x00387, 0x0043F, 0x00398, 0x00478, 0x00420,
|
72
|
+
0x00D86, 0x008C0, 0x0112D, 0x02F68, 0x01E4E, 0x00541, 0x0051B, 0x00CCE,
|
73
|
+
0x0079E, 0x00376, 0x003FF, 0x00458, 0x00435, 0x00412, 0x00425, 0x0042F,
|
74
|
+
0x005CC, 0x003E9, 0x00448, 0x00393, 0x0041C, 0x003E3, 0x0042E, 0x0036C,
|
75
|
+
0x00457, 0x00353, 0x00423, 0x00325, 0x00458, 0x0039B, 0x0044F, 0x00331,
|
76
|
+
0x0076B, 0x00750, 0x003D0, 0x00349, 0x00467, 0x003BC, 0x00487, 0x003B6,
|
77
|
+
0x01E6F, 0x003BA, 0x00509, 0x003A5, 0x00467, 0x00C87, 0x003FC, 0x0039F,
|
78
|
+
0x0054B, 0x00300, 0x00410, 0x002E9, 0x003B8, 0x00325, 0x00431, 0x002E4,
|
79
|
+
0x003F5, 0x00325, 0x003F0, 0x0031C, 0x003E4, 0x00421, 0x02CC1, 0x034C0
|
80
|
+
};
|
81
|
+
|
82
|
+
|
83
|
+
//
|
84
|
+
// static Huffman tree
|
85
|
+
//
|
86
|
+
static tree_t huffTree;
|
87
|
+
|
88
|
+
//
|
89
|
+
// received from MSG_* code
|
90
|
+
//
|
91
|
+
static int huffBitPos;
|
92
|
+
|
93
|
+
|
94
|
+
/*
|
95
|
+
=======================================================================================
|
96
|
+
|
97
|
+
HUFFMAN TREE CONSTRUCTION
|
98
|
+
|
99
|
+
=======================================================================================
|
100
|
+
*/
|
101
|
+
|
102
|
+
|
103
|
+
/*
|
104
|
+
============
|
105
|
+
Huff_PrepareTree
|
106
|
+
============
|
107
|
+
*/
|
108
|
+
static ID_INLINE void Huff_PrepareTree( tree_t tree ) {
|
109
|
+
void **node;
|
110
|
+
|
111
|
+
memset( tree, 0, sizeof( tree_t ) );
|
112
|
+
|
113
|
+
// create first node
|
114
|
+
node = &tree[263];
|
115
|
+
tree[0] = NODE((VALUE(tree[0])+1));
|
116
|
+
|
117
|
+
node[7] = NODE_NONE;
|
118
|
+
tree[2] = node;
|
119
|
+
tree[3] = node;
|
120
|
+
tree[4] = node;
|
121
|
+
tree[261] = node;
|
122
|
+
}
|
123
|
+
|
124
|
+
/*
|
125
|
+
============
|
126
|
+
Huff_GetNode
|
127
|
+
============
|
128
|
+
*/
|
129
|
+
static ID_INLINE void **Huff_GetNode( void **tree ) {
|
130
|
+
void **node;
|
131
|
+
int value;
|
132
|
+
|
133
|
+
node = tree[262];
|
134
|
+
if( !node ) {
|
135
|
+
value = VALUE( tree[1] )++;
|
136
|
+
node = &tree[value + 6407];
|
137
|
+
return node;
|
138
|
+
}
|
139
|
+
|
140
|
+
tree[262] = node[0];
|
141
|
+
return node;
|
142
|
+
}
|
143
|
+
|
144
|
+
/*
|
145
|
+
============
|
146
|
+
Huff_Swap
|
147
|
+
============
|
148
|
+
*/
|
149
|
+
static ID_INLINE void Huff_Swap( void **tree1, void **tree2, void **tree3 ) {
|
150
|
+
void **a, **b;
|
151
|
+
|
152
|
+
a = tree2[2];
|
153
|
+
if( a ) {
|
154
|
+
if( a[0] == tree2 ) {
|
155
|
+
a[0] = tree3;
|
156
|
+
} else {
|
157
|
+
a[1] = tree3;
|
158
|
+
}
|
159
|
+
} else {
|
160
|
+
tree1[2] = tree3;
|
161
|
+
}
|
162
|
+
|
163
|
+
b = tree3[2];
|
164
|
+
|
165
|
+
if( b ) {
|
166
|
+
if( b[0] == tree3 ) {
|
167
|
+
b[0] = tree2;
|
168
|
+
tree2[2] = b;
|
169
|
+
tree3[2] = a;
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
b[1] = tree2;
|
174
|
+
tree2[2] = b;
|
175
|
+
tree3[2] = a;
|
176
|
+
return;
|
177
|
+
}
|
178
|
+
|
179
|
+
tree1[2] = tree2;
|
180
|
+
tree2[2] = NULL;
|
181
|
+
tree3[2] = a;
|
182
|
+
}
|
183
|
+
|
184
|
+
/*
|
185
|
+
============
|
186
|
+
Huff_SwapTrees
|
187
|
+
============
|
188
|
+
*/
|
189
|
+
static ID_INLINE void Huff_SwapTrees( void **tree1, void **tree2 ) {
|
190
|
+
void **temp;
|
191
|
+
|
192
|
+
temp = tree1[3];
|
193
|
+
tree1[3] = tree2[3];
|
194
|
+
tree2[3] = temp;
|
195
|
+
|
196
|
+
temp = tree1[4];
|
197
|
+
tree1[4] = tree2[4];
|
198
|
+
tree2[4] = temp;
|
199
|
+
|
200
|
+
if( tree1[3] == tree1 ) {
|
201
|
+
tree1[3] = tree2;
|
202
|
+
}
|
203
|
+
|
204
|
+
if( tree2[3] == tree2 ) {
|
205
|
+
tree2[3] = tree1;
|
206
|
+
}
|
207
|
+
|
208
|
+
temp = tree1[3];
|
209
|
+
if( temp ) {
|
210
|
+
temp[4] = tree1;
|
211
|
+
}
|
212
|
+
|
213
|
+
temp = tree2[3];
|
214
|
+
if( temp ) {
|
215
|
+
temp[4] = tree2;
|
216
|
+
}
|
217
|
+
|
218
|
+
temp = tree1[4];
|
219
|
+
if( temp ) {
|
220
|
+
temp[3] = tree1;
|
221
|
+
}
|
222
|
+
|
223
|
+
temp = tree2[4];
|
224
|
+
if( temp ) {
|
225
|
+
temp[3] = tree2;
|
226
|
+
}
|
227
|
+
|
228
|
+
}
|
229
|
+
|
230
|
+
/*
|
231
|
+
============
|
232
|
+
Huff_DeleteNode
|
233
|
+
============
|
234
|
+
*/
|
235
|
+
static ID_INLINE void Huff_DeleteNode( void **tree1, void **tree2 ) {
|
236
|
+
tree2[0] = tree1[262];
|
237
|
+
tree1[262] = tree2;
|
238
|
+
}
|
239
|
+
|
240
|
+
/*
|
241
|
+
============
|
242
|
+
Huff_IncrementFreq_r
|
243
|
+
============
|
244
|
+
*/
|
245
|
+
static void Huff_IncrementFreq_r( void **tree1, void **tree2 ) {
|
246
|
+
void **a, **b;
|
247
|
+
|
248
|
+
if( !tree2 ) {
|
249
|
+
return;
|
250
|
+
}
|
251
|
+
|
252
|
+
a = tree2[3];
|
253
|
+
if( a && a[6] == tree2[6] ) {
|
254
|
+
b = tree2[5];
|
255
|
+
if( b[0] != tree2[2] ) {
|
256
|
+
Huff_Swap( tree1, b[0], tree2 );
|
257
|
+
}
|
258
|
+
Huff_SwapTrees( b[0], tree2 );
|
259
|
+
}
|
260
|
+
|
261
|
+
a = tree2[4];
|
262
|
+
if( a && a[6] == tree2[6] ) {
|
263
|
+
b = tree2[5];
|
264
|
+
b[0] = a;
|
265
|
+
} else {
|
266
|
+
a = tree2[5];
|
267
|
+
a[0] = 0;
|
268
|
+
Huff_DeleteNode( tree1, a );
|
269
|
+
}
|
270
|
+
|
271
|
+
|
272
|
+
VALUE( tree2[6] )++;
|
273
|
+
a = tree2[3];
|
274
|
+
if( a && a[6] == tree2[6] ) {
|
275
|
+
tree2[5] = a[5];
|
276
|
+
} else {
|
277
|
+
a = Huff_GetNode( tree1 );
|
278
|
+
tree2[5] = a;
|
279
|
+
a[0] = tree2;
|
280
|
+
}
|
281
|
+
|
282
|
+
if( tree2[2] ) {
|
283
|
+
Huff_IncrementFreq_r( tree1, tree2[2] );
|
284
|
+
|
285
|
+
if( tree2[4] == tree2[2] ) {
|
286
|
+
Huff_SwapTrees( tree2, tree2[2] );
|
287
|
+
a = tree2[5];
|
288
|
+
|
289
|
+
if( a[0] == tree2 ) {
|
290
|
+
a[0] = tree2[2];
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
/*
|
297
|
+
============
|
298
|
+
Huff_AddReference
|
299
|
+
|
300
|
+
Insert 'ch' into the tree or increment it's frequency
|
301
|
+
============
|
302
|
+
*/
|
303
|
+
static void Huff_AddReference( void **tree, int ch ) {
|
304
|
+
void **a, **b, **c, **d;
|
305
|
+
int value;
|
306
|
+
|
307
|
+
ch &= 255;
|
308
|
+
if( tree[ch + 5] ) {
|
309
|
+
Huff_IncrementFreq_r( tree, tree[ch + 5] );
|
310
|
+
return; // already added
|
311
|
+
}
|
312
|
+
|
313
|
+
value = VALUE( tree[0] )++;
|
314
|
+
b = &tree[value * 8 + 263];
|
315
|
+
|
316
|
+
value = VALUE( tree[0] )++;
|
317
|
+
a = &tree[value * 8 + 263];
|
318
|
+
|
319
|
+
a[7] = NODE_NEXT;
|
320
|
+
a[6] = NODE_START;
|
321
|
+
d = tree[3];
|
322
|
+
a[3] = d[3];
|
323
|
+
if( a[3] ) {
|
324
|
+
d = a[3];
|
325
|
+
d[4] = a;
|
326
|
+
d = a[3];
|
327
|
+
if( d[6] == NODE_START ) {
|
328
|
+
a[5] = d[5];
|
329
|
+
} else {
|
330
|
+
d = Huff_GetNode( tree );
|
331
|
+
a[5] = d;
|
332
|
+
d[0] = a;
|
333
|
+
}
|
334
|
+
} else {
|
335
|
+
d = Huff_GetNode( tree );
|
336
|
+
a[5] = d;
|
337
|
+
d[0] = a;
|
338
|
+
|
339
|
+
}
|
340
|
+
|
341
|
+
d = tree[3];
|
342
|
+
d[3] = a;
|
343
|
+
a[4] = tree[3];
|
344
|
+
b[7] = NODE( ch );
|
345
|
+
b[6] = NODE_START;
|
346
|
+
d = tree[3];
|
347
|
+
b[3] = d[3];
|
348
|
+
if( b[3] ) {
|
349
|
+
d = b[3];
|
350
|
+
d[4] = b;
|
351
|
+
if( d[6] == NODE_START ) {
|
352
|
+
b[5] = d[5];
|
353
|
+
} else {
|
354
|
+
d = Huff_GetNode( tree );
|
355
|
+
b[5] = d;
|
356
|
+
d[0] = a;
|
357
|
+
}
|
358
|
+
} else {
|
359
|
+
d = Huff_GetNode( tree );
|
360
|
+
b[5] = d;
|
361
|
+
d[0] = b;
|
362
|
+
}
|
363
|
+
|
364
|
+
d = tree[3];
|
365
|
+
d[3] = b;
|
366
|
+
b[4] = tree[3];
|
367
|
+
b[1] = NULL;
|
368
|
+
b[0] = NULL;
|
369
|
+
d = tree[3];
|
370
|
+
c = d[2];
|
371
|
+
if( c ) {
|
372
|
+
if( c[0] == tree[3] ) {
|
373
|
+
c[0] = a;
|
374
|
+
} else {
|
375
|
+
c[1] = a;
|
376
|
+
}
|
377
|
+
} else {
|
378
|
+
tree[2] = a;
|
379
|
+
}
|
380
|
+
|
381
|
+
a[1] = b;
|
382
|
+
d = tree[3];
|
383
|
+
a[0] = d;
|
384
|
+
a[2] = d[2];
|
385
|
+
b[2] = a;
|
386
|
+
d = tree[3];
|
387
|
+
d[2] = a;
|
388
|
+
tree[ch + 5] = b;
|
389
|
+
|
390
|
+
Huff_IncrementFreq_r( tree, a[2] );
|
391
|
+
}
|
392
|
+
|
393
|
+
/*
|
394
|
+
=======================================================================================
|
395
|
+
|
396
|
+
BITSTREAM I/O
|
397
|
+
|
398
|
+
=======================================================================================
|
399
|
+
*/
|
400
|
+
|
401
|
+
/*
|
402
|
+
============
|
403
|
+
Huff_EmitBit
|
404
|
+
|
405
|
+
Put one bit into buffer
|
406
|
+
============
|
407
|
+
*/
|
408
|
+
static ID_INLINE void Huff_EmitBit( int bit, byte *buffer ) {
|
409
|
+
if( !(huffBitPos & 7) ) {
|
410
|
+
buffer[huffBitPos >> 3] = 0;
|
411
|
+
}
|
412
|
+
|
413
|
+
buffer[huffBitPos >> 3] |= bit << (huffBitPos & 7);
|
414
|
+
huffBitPos++;
|
415
|
+
}
|
416
|
+
|
417
|
+
/*
|
418
|
+
============
|
419
|
+
Huff_GetBit
|
420
|
+
|
421
|
+
Read one bit from buffer
|
422
|
+
============
|
423
|
+
*/
|
424
|
+
static ID_INLINE int Huff_GetBit( byte *buffer ) {
|
425
|
+
int bit;
|
426
|
+
|
427
|
+
bit = buffer[huffBitPos >> 3] >> (huffBitPos & 7);
|
428
|
+
huffBitPos++;
|
429
|
+
|
430
|
+
return (bit & 1);
|
431
|
+
}
|
432
|
+
|
433
|
+
/*
|
434
|
+
============
|
435
|
+
Huff_EmitPathToByte
|
436
|
+
============
|
437
|
+
*/
|
438
|
+
static ID_INLINE void Huff_EmitPathToByte( void **tree, void **subtree, byte *buffer ) {
|
439
|
+
if( tree[2] ) {
|
440
|
+
Huff_EmitPathToByte( tree[2], tree, buffer );
|
441
|
+
}
|
442
|
+
|
443
|
+
if( !subtree ) {
|
444
|
+
return;
|
445
|
+
}
|
446
|
+
|
447
|
+
//
|
448
|
+
// emit tree walking control bits
|
449
|
+
//
|
450
|
+
if( tree[1] == subtree ) {
|
451
|
+
Huff_EmitBit( 1, buffer );
|
452
|
+
} else {
|
453
|
+
Huff_EmitBit( 0, buffer );
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
/*
|
458
|
+
============
|
459
|
+
Huff_GetByteFromTree
|
460
|
+
|
461
|
+
Get one byte using dynamic or static tree
|
462
|
+
============
|
463
|
+
*/
|
464
|
+
static ID_INLINE int Huff_GetByteFromTree( void **tree, byte *buffer ) {
|
465
|
+
if( !tree ) {
|
466
|
+
return 0;
|
467
|
+
}
|
468
|
+
|
469
|
+
//
|
470
|
+
// walk through the tree until we get a value
|
471
|
+
//
|
472
|
+
while( tree[7] == NODE_NEXT ) {
|
473
|
+
if( !Huff_GetBit( buffer ) ) {
|
474
|
+
tree = tree[0];
|
475
|
+
} else {
|
476
|
+
tree = tree[1];
|
477
|
+
}
|
478
|
+
|
479
|
+
if( !tree ) {
|
480
|
+
return 0;
|
481
|
+
}
|
482
|
+
}
|
483
|
+
|
484
|
+
return VALUE( tree[7] );
|
485
|
+
}
|
486
|
+
|
487
|
+
/*
|
488
|
+
=======================================================================================
|
489
|
+
|
490
|
+
PUBLIC INTERFACE
|
491
|
+
|
492
|
+
=======================================================================================
|
493
|
+
*/
|
494
|
+
|
495
|
+
/*
|
496
|
+
============
|
497
|
+
Huff_EmitByte
|
498
|
+
============
|
499
|
+
*/
|
500
|
+
void Huff_EmitByte( int ch, byte *buffer, int *count ) {
|
501
|
+
huffBitPos = *count;
|
502
|
+
Huff_EmitPathToByte( huffTree[ch + 5], NULL, buffer );
|
503
|
+
*count = huffBitPos;
|
504
|
+
}
|
505
|
+
|
506
|
+
/*
|
507
|
+
============
|
508
|
+
Huff_GetByte
|
509
|
+
============
|
510
|
+
*/
|
511
|
+
int Huff_GetByte( byte *buffer, int *count ) {
|
512
|
+
int ch;
|
513
|
+
|
514
|
+
huffBitPos = *count;
|
515
|
+
ch = Huff_GetByteFromTree( huffTree[2], buffer );
|
516
|
+
*count = huffBitPos;
|
517
|
+
|
518
|
+
return ch;
|
519
|
+
}
|
520
|
+
|
521
|
+
/*
|
522
|
+
============
|
523
|
+
Huff_Init
|
524
|
+
============
|
525
|
+
*/
|
526
|
+
void Huff_Init( void ) {
|
527
|
+
int i, j;
|
528
|
+
|
529
|
+
// build empty tree
|
530
|
+
Huff_PrepareTree( huffTree );
|
531
|
+
|
532
|
+
// add all pre-defined byte references
|
533
|
+
for( i=0 ; i<256 ; i++ ) {
|
534
|
+
for( j=0 ; j<huffCounts[i] ; j++ ) {
|
535
|
+
Huff_AddReference( huffTree, i );
|
536
|
+
}
|
537
|
+
}
|
538
|
+
|
539
|
+
}
|
540
|
+
|
541
|
+
|
542
|
+
|