demo-reader 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|