Quake II RTX doxygen  1.0 dev
All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
models.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2008 Andrey Nazarov
4 Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved.
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "shared/shared.h"
22 #include "shared/list.h"
23 #include "common/common.h"
24 #include "common/files.h"
25 #include "system/hunk.h"
26 #include "format/md2.h"
27 #if USE_MD3
28 #include "format/md3.h"
29 #endif
30 #include "format/sp2.h"
31 #include "refresh/images.h"
32 #include "refresh/models.h"
33 
34 // during registration it is possible to have more models than could actually
35 // be referenced during gameplay, because we don't want to free anything until
36 // we are sure we won't need it.
37 #define MAX_RMODELS (MAX_MODELS * 2)
38 
41 
42 extern cvar_t *vid_rtx;
43 
44 static model_t *MOD_Alloc(void)
45 {
46  model_t *model;
47  int i;
48 
49  for (i = 0, model = r_models; i < r_numModels; i++, model++) {
50  if (!model->type) {
51  break;
52  }
53  }
54 
55  if (i == r_numModels) {
56  if (r_numModels == MAX_RMODELS) {
57  return NULL;
58  }
59  r_numModels++;
60  }
61 
62  return model;
63 }
64 
65 static model_t *MOD_Find(const char *name)
66 {
67  model_t *model;
68  int i;
69 
70  for (i = 0, model = r_models; i < r_numModels; i++, model++) {
71  if (!model->type) {
72  continue;
73  }
74  if (!FS_pathcmp(model->name, name)) {
75  return model;
76  }
77  }
78 
79  return NULL;
80 }
81 
82 static void MOD_List_f(void)
83 {
84  static const char types[4] = "FASE";
85  int i, count;
86  model_t *model;
87  size_t bytes;
88 
89  Com_Printf("------------------\n");
90  bytes = count = 0;
91 
92  for (i = 0, model = r_models; i < r_numModels; i++, model++) {
93  if (!model->type) {
94  continue;
95  }
96  Com_Printf("%c %8"PRIz" : %s\n", types[model->type],
97  model->hunk.mapped, model->name);
98  bytes += model->hunk.mapped;
99  count++;
100  }
101  Com_Printf("Total models: %d (out of %d slots)\n", count, r_numModels);
102  Com_Printf("Total resident: %"PRIz"\n", bytes);
103 }
104 
105 void MOD_FreeUnused(void)
106 {
107  model_t *model;
108  int i;
109 
110  for (i = 0, model = r_models; i < r_numModels; i++, model++) {
111  if (!model->type) {
112  continue;
113  }
114  if (model->registration_sequence == registration_sequence) {
115  // make sure it is paged in
116  Com_PageInMemory(model->hunk.base, model->hunk.cursize);
117  } else {
118  // don't need this model
119  Hunk_Free(&model->hunk);
120  memset(model, 0, sizeof(*model));
121  }
122  }
123 }
124 
125 void MOD_FreeAll(void)
126 {
127  model_t *model;
128  int i;
129 
130  for (i = 0, model = r_models; i < r_numModels; i++, model++) {
131  if (!model->type) {
132  continue;
133  }
134 
135  Hunk_Free(&model->hunk);
136  memset(model, 0, sizeof(*model));
137  }
138 
139  r_numModels = 0;
140 }
141 
142 qerror_t MOD_ValidateMD2(dmd2header_t *header, size_t length)
143 {
144  size_t end;
145 
146  // check ident and version
147  if (header->ident != MD2_IDENT)
148  return Q_ERR_UNKNOWN_FORMAT;
149  if (header->version != MD2_VERSION)
150  return Q_ERR_UNKNOWN_FORMAT;
151 
152  // check triangles
153  if (header->num_tris < 1)
154  return Q_ERR_TOO_FEW;
155  if (header->num_tris > MD2_MAX_TRIANGLES)
156  return Q_ERR_TOO_MANY;
157 
158  end = header->ofs_tris + sizeof(dmd2triangle_t) * header->num_tris;
159  if (header->ofs_tris < sizeof(header) || end < header->ofs_tris || end > length)
160  return Q_ERR_BAD_EXTENT;
161 
162  // check st
163  if (header->num_st < 3)
164  return Q_ERR_TOO_FEW;
165  if (header->num_st > MAX_ALIAS_VERTS)
166  return Q_ERR_TOO_MANY;
167 
168  end = header->ofs_st + sizeof(dmd2stvert_t) * header->num_st;
169  if (header->ofs_st < sizeof(header) || end < header->ofs_st || end > length)
170  return Q_ERR_BAD_EXTENT;
171 
172  // check xyz and frames
173  if (header->num_xyz < 3)
174  return Q_ERR_TOO_FEW;
175  if (header->num_xyz > MAX_ALIAS_VERTS)
176  return Q_ERR_TOO_MANY;
177  if (header->num_frames < 1)
178  return Q_ERR_TOO_FEW;
179  if (header->num_frames > MD2_MAX_FRAMES)
180  return Q_ERR_TOO_MANY;
181 
182  end = sizeof(dmd2frame_t) + (header->num_xyz - 1) * sizeof(dmd2trivertx_t);
183  if (header->framesize < end || header->framesize > MD2_MAX_FRAMESIZE)
184  return Q_ERR_BAD_EXTENT;
185 
186  end = header->ofs_frames + (size_t)header->framesize * header->num_frames;
187  if (header->ofs_frames < sizeof(header) || end < header->ofs_frames || end > length)
188  return Q_ERR_BAD_EXTENT;
189 
190  // check skins
191  if (header->num_skins) {
192  if (header->num_skins > MAX_ALIAS_SKINS)
193  return Q_ERR_TOO_MANY;
194 
195  end = header->ofs_skins + (size_t)MD2_MAX_SKINNAME * header->num_skins;
196  if (header->ofs_skins < sizeof(header) || end < header->ofs_skins || end > length)
197  return Q_ERR_BAD_EXTENT;
198  }
199 
200  if (header->skinwidth < 1 || header->skinwidth > MD2_MAX_SKINWIDTH)
201  return Q_ERR_INVALID_FORMAT;
202  if (header->skinheight < 1 || header->skinheight > MD2_MAX_SKINHEIGHT)
203  return Q_ERR_INVALID_FORMAT;
204 
205  return Q_ERR_SUCCESS;
206 }
207 
208 static model_class_t
209 get_model_class(const char *name)
210 {
211  if (!strcmp(name, "models/objects/explode/tris.md2"))
212  return MCLASS_EXPLOSION;
213  else if (!strcmp(name, "models/objects/r_explode/tris.md2"))
214  return MCLASS_EXPLOSION;
215  else if (!strcmp(name, "models/objects/flash/tris.md2"))
216  return MCLASS_SMOKE;
217  else if (!strcmp(name, "models/objects/smoke/tris.md2"))
218  return MCLASS_SMOKE;
219  else if (!strcmp(name, "models/objects/minelite/light2/tris.md2"))
220  return MCLASS_STATIC_LIGHT;
221  else if (!strcmp(name, "models/objects/flare/tris.md2"))
222  return MCLASS_FLARE;
223  else
224  return MCLASS_REGULAR;
225 }
226 
227 static qerror_t MOD_LoadSP2(model_t *model, const void *rawdata, size_t length)
228 {
229  dsp2header_t header;
230  dsp2frame_t *src_frame;
231  mspriteframe_t *dst_frame;
232  unsigned w, h, x, y;
233  char buffer[SP2_MAX_FRAMENAME];
234  int i;
235 
236  if (length < sizeof(header))
237  return Q_ERR_FILE_TOO_SMALL;
238 
239  // byte swap the header
240  header = *(dsp2header_t *)rawdata;
241  for (i = 0; i < sizeof(header) / 4; i++) {
242  ((uint32_t *)&header)[i] = LittleLong(((uint32_t *)&header)[i]);
243  }
244 
245  if (header.ident != SP2_IDENT)
246  return Q_ERR_UNKNOWN_FORMAT;
247  if (header.version != SP2_VERSION)
248  return Q_ERR_UNKNOWN_FORMAT;
249  if (header.numframes < 1) {
250  // empty models draw nothing
251  model->type = MOD_EMPTY;
252  return Q_ERR_SUCCESS;
253  }
254  if (header.numframes > SP2_MAX_FRAMES)
255  return Q_ERR_TOO_MANY;
256  if (sizeof(dsp2header_t) + sizeof(dsp2frame_t) * header.numframes > length)
257  return Q_ERR_BAD_EXTENT;
258 
259  Hunk_Begin(&model->hunk, 0x10000);
260  model->type = MOD_SPRITE;
261 
262  model->spriteframes = MOD_Malloc(sizeof(mspriteframe_t) * header.numframes);
263  model->numframes = header.numframes;
264 
265  src_frame = (dsp2frame_t *)((byte *)rawdata + sizeof(dsp2header_t));
266  dst_frame = model->spriteframes;
267  for (i = 0; i < header.numframes; i++) {
268  w = LittleLong(src_frame->width);
269  h = LittleLong(src_frame->height);
270  if (w < 1 || h < 1 || w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE) {
271  Com_WPrintf("%s has bad frame dimensions\n", model->name);
272  w = 1;
273  h = 1;
274  }
275  dst_frame->width = w;
276  dst_frame->height = h;
277 
278  // FIXME: are these signed?
279  x = LittleLong(src_frame->origin_x);
280  y = LittleLong(src_frame->origin_y);
281  if (x > 8192 || y > 8192) {
282  Com_WPrintf("%s has bad frame origin\n", model->name);
283  x = y = 0;
284  }
285  dst_frame->origin_x = x;
286  dst_frame->origin_y = y;
287 
288  if (!Q_memccpy(buffer, src_frame->name, 0, sizeof(buffer))) {
289  Com_WPrintf("%s has bad frame name\n", model->name);
290  dst_frame->image = R_NOTEXTURE;
291  } else {
292  FS_NormalizePath(buffer, buffer);
293  dst_frame->image = IMG_Find(buffer, IT_SPRITE, IF_SRGB);
294  }
295 
296  src_frame++;
297  dst_frame++;
298  }
299 
300  Hunk_End(&model->hunk);
301 
302  return Q_ERR_SUCCESS;
303 }
304 
305 qhandle_t R_RegisterModel(const char *name)
306 {
307  char normalized[MAX_QPATH];
308  qhandle_t index;
309  size_t namelen;
310  ssize_t filelen;
311  model_t *model;
312  byte *rawdata = NULL;
313  uint32_t ident;
314  mod_load_t load;
315  qerror_t ret;
316 
317  // empty names are legal, silently ignore them
318  if (!*name)
319  return 0;
320 
321  if (*name == '*') {
322  // inline bsp model
323  index = atoi(name + 1);
324  return ~index;
325  }
326 
327  // normalize the path
328  namelen = FS_NormalizePathBuffer(normalized, name, MAX_QPATH);
329 
330  // this should never happen
331  if (namelen >= MAX_QPATH)
332  Com_Error(ERR_DROP, "%s: oversize name", __func__);
333 
334  // normalized to empty name?
335  if (namelen == 0) {
336  Com_DPrintf("%s: empty name\n", __func__);
337  return 0;
338  }
339 
340  // see if it's already loaded
341  model = MOD_Find(normalized);
342  if (model) {
343  MOD_Reference(model);
344  goto done;
345  }
346 
347  char* extension = normalized + namelen - 4;
348  if (namelen > 4 && (strcmp(extension, ".md2") == 0) && vid_rtx->integer)
349  {
350  memcpy(extension, ".md3", 4);
351 
352  filelen = FS_LoadFile(normalized, (void **)&rawdata);
353 
354  memcpy(extension, ".md2", 4);
355  }
356 
357  if (!rawdata)
358  {
359  filelen = FS_LoadFile(normalized, (void **)&rawdata);
360  if (!rawdata) {
361  // don't spam about missing models
362  if (filelen == Q_ERR_NOENT) {
363  return 0;
364  }
365 
366  ret = filelen;
367  goto fail1;
368  }
369  }
370 
371  if (filelen < 4) {
372  ret = Q_ERR_FILE_TOO_SMALL;
373  goto fail2;
374  }
375 
376  // check ident
377  ident = LittleLong(*(uint32_t *)rawdata);
378  switch (ident) {
379  case MD2_IDENT:
380  load = MOD_LoadMD2;
381  break;
382 #if USE_MD3
383  case MD3_IDENT:
384  load = MOD_LoadMD3;
385  break;
386 #endif
387  case SP2_IDENT:
388  load = MOD_LoadSP2;
389  break;
390  default:
391  ret = Q_ERR_UNKNOWN_FORMAT;
392  goto fail2;
393  }
394 
395  model = MOD_Alloc();
396  if (!model) {
397  ret = Q_ERR_OUT_OF_SLOTS;
398  goto fail2;
399  }
400 
401  memcpy(model->name, normalized, namelen + 1);
402  model->registration_sequence = registration_sequence;
403 
404  ret = load(model, rawdata, filelen);
405 
406  FS_FreeFile(rawdata);
407 
408  if (ret) {
409  memset(model, 0, sizeof(*model));
410  goto fail1;
411  }
412 
413  model->model_class = get_model_class(model->name);
414 
415 done:
416  index = (model - r_models) + 1;
417 #if USE_REF == REF_VKPT
418  extern int register_model_dirty;
420 #endif
421  return index;
422 
423 fail2:
424  FS_FreeFile(rawdata);
425 fail1:
426  Com_EPrintf("Couldn't load %s: %s\n", normalized, Q_ErrorString(ret));
427  return 0;
428 }
429 
430 model_t *MOD_ForHandle(qhandle_t h)
431 {
432  model_t *model;
433 
434  if (!h) {
435  return NULL;
436  }
437 
438  if (h < 0 || h > r_numModels) {
439  Com_Error(ERR_DROP, "%s: %d out of range", __func__, h);
440  }
441 
442  model = &r_models[h - 1];
443  if (!model->type) {
444  return NULL;
445  }
446 
447  return model;
448 }
449 
450 void MOD_Init(void)
451 {
452  if (r_numModels) {
453  Com_Error(ERR_FATAL, "%s: %d models not freed", __func__, r_numModels);
454  }
455 
456  Cmd_AddCommand("modellist", MOD_List_f);
457 }
458 
459 void MOD_Shutdown(void)
460 {
461  MOD_FreeAll();
462  Cmd_RemoveCommand("modellist");
463 }
464 
MOD_Reference
void(* MOD_Reference)(model_t *model)
Definition: refresh.c:438
register_model_dirty
int register_model_dirty
Definition: main.c:431
vid_rtx
cvar_t * vid_rtx
Definition: refresh.c:30
Hunk_Begin
void Hunk_Begin(memhunk_t *hunk, size_t maxsize)
Definition: hunk.c:23
MOD_ForHandle
model_t * MOD_ForHandle(qhandle_t h)
Definition: models.c:430
Cmd_AddCommand
void Cmd_AddCommand(const char *name, xcommand_t function)
Definition: cmd.c:1562
Q_ErrorString
const char * Q_ErrorString(qerror_t error)
Definition: error.c:51
MOD_Init
void MOD_Init(void)
Definition: models.c:450
MOD_Alloc
static model_t * MOD_Alloc(void)
Definition: models.c:44
MOD_List_f
static void MOD_List_f(void)
Definition: models.c:82
MOD_Find
static model_t * MOD_Find(const char *name)
Definition: models.c:65
MOD_ValidateMD2
qerror_t MOD_ValidateMD2(dmd2header_t *header, size_t length)
Definition: models.c:142
r_numModels
int r_numModels
Definition: models.c:40
MOD_FreeUnused
void MOD_FreeUnused(void)
Definition: models.c:105
Hunk_Free
void Hunk_Free(memhunk_t *hunk)
Definition: hunk.c:75
FS_NormalizePathBuffer
size_t FS_NormalizePathBuffer(char *out, const char *in, size_t size)
Definition: files.c:400
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
R_RegisterModel
qhandle_t R_RegisterModel(const char *name)
Definition: models.c:305
Cmd_RemoveCommand
void Cmd_RemoveCommand(const char *name)
Definition: cmd.c:1593
load
qerror_t(* load)(byte *, size_t, image_t *, byte **)
Definition: images.c:658
MOD_LoadMD2
qerror_t(* MOD_LoadMD2)(model_t *model, const void *rawdata, size_t length)
Definition: refresh.c:434
MOD_FreeAll
void MOD_FreeAll(void)
Definition: models.c:125
Hunk_End
void Hunk_End(memhunk_t *hunk)
Definition: hunk.c:66
MOD_Shutdown
void MOD_Shutdown(void)
Definition: models.c:459
IMG_Find
image_t * IMG_Find(const char *name, imagetype_t type, imageflags_t flags)
Definition: images.c:1122
MOD_LoadSP2
static qerror_t MOD_LoadSP2(model_t *model, const void *rawdata, size_t length)
Definition: models.c:227
MAX_RMODELS
#define MAX_RMODELS
Definition: models.c:37
registration_sequence
int registration_sequence
Definition: main.c:34
Q_memccpy
void * Q_memccpy(void *dst, const void *src, int c, size_t size)
Definition: shared.c:895
Com_PageInMemory
void Com_PageInMemory(void *buffer, size_t size)
Definition: utils.c:383
get_model_class
static model_class_t get_model_class(const char *name)
Definition: models.c:209
FS_NormalizePath
size_t FS_NormalizePath(char *out, const char *in)
Definition: files.c:331
r_models
model_t r_models[MAX_RMODELS]
Definition: models.c:39