-
Notifications
You must be signed in to change notification settings - Fork 1
/
bsp.c
244 lines (207 loc) · 6.33 KB
/
bsp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include "bsp.h"
#include "texture.h"
#include "utils.h"
// for exit
#include <stdlib.h>
// for memset
#include <string.h>
// for UINT_MAX
#include <limits.h>
// defines for lump types we care to parse
#define LUMP_ENTITIES 0
#define LUMP_TEXTURES 2
struct s_bsp * read_bsp(const char * path)
{
unsigned char buf[8];
FILE *fp = u_fopen(path, "rb");
// Check the BSP header for v30
u_fread(buf, 4, fp);
unsigned int version = parseU32(buf);
if (version != 30) {
fprintf( stderr, "%s does not appear to be a BSPv30 file (got %u)\n", path, version );
exit(EXIT_FAILURE);
}
// create a struct to hold our info
struct s_bsp *ret = (struct s_bsp *)u_malloc(sizeof(struct s_bsp));
// get all lump info
unsigned int lumpOffset[15];
for (int i = 0; i < 15; i ++) {
u_fread(buf, 8, fp);
lumpOffset[i] = parseU32(buf);
ret->raw_lump_size[i] = parseU32(buf+4);
}
// use lumpOffset to calculate lump order
unsigned int prevOffset = 0;
for (int i = 0; i < 15; i ++) {
unsigned int nextLump = 0;
unsigned int nextOffset = UINT_MAX;
for (int j = 0; j < 15; j ++) {
if (lumpOffset[j] > prevOffset && lumpOffset[j] < nextOffset) {
nextLump = j;
nextOffset = lumpOffset[j];
}
}
prevOffset = nextOffset;
ret->lump_order[i] = nextLump;
}
// ///////////////////////
// read all lumps except those needing special handling
for (int i = 0; i < 15; i ++) {
if (i == LUMP_ENTITIES || i == LUMP_TEXTURES) continue;
u_fseek( fp, lumpOffset[i] );
ret->raw_lump[i] = (unsigned char*)u_malloc( ret->raw_lump_size[i] );
u_fread(ret->raw_lump[i], ret->raw_lump_size[i], fp );
}
// ///////////////////////
// entity lump
u_fseek( fp, lumpOffset[LUMP_ENTITIES] );
ret->entity_lump_size = ret->raw_lump_size[LUMP_ENTITIES];
ret->entity_lump = (char *)u_malloc( ret->entity_lump_size );
u_fread(ret->entity_lump, ret->entity_lump_size, fp );
// ///////////////////////
// read the Textures block
u_fseek( fp, lumpOffset[LUMP_TEXTURES] );
u_fread(buf, 4, fp);
ret->texture_count = parseU32(buf);
ret->textures = (struct s_texture **)u_malloc(ret->texture_count * sizeof(struct texture*));
// get the offsets to each texture
unsigned int *mipTexOffsets = (unsigned int*)u_malloc(ret->texture_count * sizeof(unsigned int));
for (unsigned int i = 0; i < ret->texture_count; i ++)
{
u_fread(buf, 4, fp);
mipTexOffsets[i] = parseU32(buf);
}
// now go read each texture
for (unsigned int i = 0; i < ret->texture_count; i ++)
{
ret->textures[i] = read_texture(fp, lumpOffset[LUMP_TEXTURES] + mipTexOffsets[i]);
}
// don't need these any more
free(mipTexOffsets);
fclose(fp);
return ret;
}
void write_bsp(const struct s_bsp *const bsp, const char* path)
{
FILE *fp = u_fopen(path, "wb");
unsigned char buf[4];
// calculate the size of the new texture block
// begin with 4 bytes (uint32 texture_count)
unsigned int texLumpSize = 4;
// then a list of offsets to textures
texLumpSize += bsp->texture_count * 4;
// finally the combined size of each texture
for (unsigned int i = 0; i < bsp->texture_count; i ++)
texLumpSize += get_texture_size(bsp->textures[i]);
// Ready to write the output BSP
// Write v30 header
packU32(buf, 30U);
u_fwrite(buf, 4, fp);
// compute lump offsets using BSP lump order
// begin with uint32 'version', plus 8 bytes for each of 15 lumps
size_t offset = 4 + (8 * 15);
size_t lumpOffset[15];
for (int a = 0; a < 15; a ++) {
// whose turn is it?
int i = bsp->lump_order[a];
// this one will be placed at the next available offset
lumpOffset[i] = offset;
// advance by the size of this block
if (i == LUMP_ENTITIES) {
offset += bsp->entity_lump_size;
} else if (i == LUMP_TEXTURES) {
offset += texLumpSize;
} else {
offset += bsp->raw_lump_size[i];
}
// all offsets should be dword-aligned
offset = (offset+3)&~3;
}
// now possible to write the directory (offsets and lengths of all)
for (int i = 0; i < 15; i ++) {
// offset
packU32(buf, lumpOffset[i]);
u_fwrite(buf, 4, fp);
// size
size_t lumpSize;
if (i == LUMP_ENTITIES) {
lumpSize = bsp->entity_lump_size;
} else if (i == LUMP_TEXTURES) {
lumpSize = texLumpSize;
} else {
lumpSize = bsp->raw_lump_size[i];
}
packU32(buf, lumpSize);
u_fwrite(buf, 4, fp);
}
// now write all lumps
for (int a = 0; a < 15; a ++) {
int i = bsp->lump_order[a];
if (i == LUMP_ENTITIES) {
// Just write the Entities lump
u_fwrite(bsp->entity_lump, bsp->entity_lump_size, fp );
} else if (i == LUMP_TEXTURES) {
// uint32 texture_count
packU32(buf, bsp->texture_count);
u_fwrite(buf, 4, fp);
// offsets to each texture
offset = 4 + (4 * bsp->texture_count);
for (unsigned int j = 0; j < bsp->texture_count; j ++)
{
packU32(buf, offset);
u_fwrite(buf, 4, fp);
offset += get_texture_size(bsp->textures[j]);
}
// texture data
for (unsigned int j = 0; j < bsp->texture_count; j ++)
{
// texture name
u_fwrite(bsp->textures[j]->name, 16, fp);
// texture width and height
packU32(buf, bsp->textures[j]->width);
u_fwrite(buf, 4, fp);
packU32(buf, bsp->textures[j]->height);
u_fwrite(buf, 4, fp);
// texture offsets?
if (bsp->textures[j]->palette) {
// this is an embedded texture
// start at the 40-byte header
offset = 40;
int width = bsp->textures[j]->width;
int height = bsp->textures[j]->height;
for (int k = 0; k < 4; k ++) {
packU32(buf, offset);
u_fwrite(buf, 4, fp);
offset += (width * height);
width >>= 1; height >>= 1;
}
// write the texture data now
width = bsp->textures[j]->width;
height = bsp->textures[j]->height;
for (int k = 0; k < 4; k ++) {
u_fwrite(bsp->textures[j]->level[k], width * height, fp);
width >>= 1; height >>= 1;
}
// finally, the palette
packU16(buf, bsp->textures[j]->palette_count);
u_fwrite(buf, 2, fp);
u_fwrite(bsp->textures[j]->palette, bsp->textures[j]->palette_count * 3, fp);
} else {
// no embedded texture, no mipmap offsets. just write 4x zero
memset(buf, 0, 4);
for (int k = 0; k < 4; k ++)
u_fwrite(buf, 4, fp);
}
}
} else {
// Raw unparsed lump, write it as-is
u_fwrite(bsp->raw_lump[i], bsp->raw_lump_size[i], fp );
}
// padding
unsigned int file_len = ftell(fp);
if (file_len & 3) {
u_fwrite("\0\0", 4 - (file_len & 3), fp);
}
}
fclose(fp);
}