cm_load.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  1. // cmodel.c -- model loading
  2. #include "cm_local.h"
  3. #include "../RMG/RM_Headers.h"
  4. void CM_LoadShaderText(bool forceReload);
  5. #ifdef BSPC
  6. void SetPlaneSignbits (cplane_t *out) {
  7. int bits, j;
  8. // for fast box on planeside test
  9. bits = 0;
  10. for (j=0 ; j<3 ; j++) {
  11. if (out->normal[j] < 0) {
  12. bits |= 1<<j;
  13. }
  14. }
  15. out->signbits = bits;
  16. }
  17. #endif //BSPC
  18. // to allow boxes to be treated as brush models, we allocate
  19. // some extra indexes along with those needed by the map
  20. #define BOX_BRUSHES 1
  21. #define BOX_SIDES 6
  22. #define BOX_LEAFS 2
  23. #define BOX_PLANES 12
  24. #define LL(x) x=LittleLong(x)
  25. clipMap_t cmg;
  26. int c_pointcontents;
  27. int c_traces, c_brush_traces, c_patch_traces;
  28. byte *cmod_base;
  29. #ifndef BSPC
  30. cvar_t *cm_noAreas;
  31. cvar_t *cm_noCurves;
  32. cvar_t *cm_playerCurveClip;
  33. #endif
  34. cmodel_t box_model;
  35. cplane_t *box_planes;
  36. cbrush_t *box_brush;
  37. int CM_OrOfAllContentsFlagsInMap;
  38. void CM_InitBoxHull (void);
  39. void CM_FloodAreaConnections (clipMap_t &cm);
  40. clipMap_t SubBSP[MAX_SUB_BSP];
  41. int NumSubBSP = 0, TotalSubModels = 0;
  42. /*
  43. ===============================================================================
  44. MAP LOADING
  45. ===============================================================================
  46. */
  47. /*
  48. =================
  49. CMod_LoadShaders
  50. =================
  51. */
  52. void CMod_LoadShaders( lump_t *l, clipMap_t &cm )
  53. {
  54. dshader_t *in;
  55. int i, count;
  56. CCMShader *out;
  57. in = (dshader_t *)(cmod_base + l->fileofs);
  58. if (l->filelen % sizeof(*in)) {
  59. Com_Error (ERR_DROP, "CMod_LoadShaders: funny lump size");
  60. }
  61. count = l->filelen / sizeof(*in);
  62. if (count < 1) {
  63. Com_Error (ERR_DROP, "Map with no shaders");
  64. }
  65. cm.shaders = (CCMShader *)Z_Malloc( (1 + count) * sizeof( *cm.shaders ), TAG_BSP, qtrue ); //+1 for the BOX_SIDES to point at
  66. cm.numShaders = count;
  67. out = cm.shaders;
  68. for ( i = 0; i < count; i++, in++, out++ )
  69. {
  70. Q_strncpyz(out->shader, in->shader, MAX_QPATH);
  71. out->contentFlags = LittleLong( in->contentFlags );
  72. out->surfaceFlags = LittleLong( in->surfaceFlags );
  73. }
  74. }
  75. /*
  76. =================
  77. CMod_LoadSubmodels
  78. =================
  79. */
  80. void CMod_LoadSubmodels( lump_t *l, clipMap_t &cm ) {
  81. dmodel_t *in;
  82. cmodel_t *out;
  83. int i, j, count;
  84. int *indexes;
  85. in = (dmodel_t *)(cmod_base + l->fileofs);
  86. if (l->filelen % sizeof(*in))
  87. Com_Error (ERR_DROP, "CMod_LoadSubmodels: funny lump size");
  88. count = l->filelen / sizeof(*in);
  89. if (count < 1) {
  90. Com_Error (ERR_DROP, "Map with no models");
  91. }
  92. //FIXME: note that MAX_SUBMODELS - 1 is used for BOX_MODEL_HANDLE, if that slot gets used, that would be bad, no?
  93. if ( count > MAX_SUBMODELS ) {
  94. Com_Error( ERR_DROP, "MAX_SUBMODELS (%d) exceeded by %d", MAX_SUBMODELS, count-MAX_SUBMODELS );
  95. }
  96. cm.cmodels = (struct cmodel_s *) Z_Malloc( count * sizeof( *cm.cmodels ), TAG_BSP, qtrue );
  97. cm.numSubModels = count;
  98. for ( i=0 ; i<count ; i++, in++, out++)
  99. {
  100. out = &cm.cmodels[i];
  101. for (j=0 ; j<3 ; j++)
  102. { // spread the mins / maxs by a pixel
  103. out->mins[j] = LittleFloat (in->mins[j]) - 1;
  104. out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
  105. }
  106. //rww - I changed this to do the &cm == &cmg check. sof2 does not have to do this,
  107. //but I think they have a different tracing system that allows it to catch subbsp
  108. //stuff without extra leaf data. The reason we have to do this for subbsp instances
  109. //is that they often are compiled in a sort of "prefab" form, so the first model isn't
  110. //necessarily the world model.
  111. if ( i == 0 && &cm == &cmg ) {
  112. continue; // world model doesn't need other info
  113. }
  114. // make a "leaf" just to hold the model's brushes and surfaces
  115. out->leaf.numLeafBrushes = LittleLong( in->numBrushes );
  116. indexes = (int *) Z_Malloc( out->leaf.numLeafBrushes * 4, TAG_BSP, qfalse);
  117. out->leaf.firstLeafBrush = indexes - cm.leafbrushes;
  118. for ( j = 0 ; j < out->leaf.numLeafBrushes ; j++ ) {
  119. indexes[j] = LittleLong( in->firstBrush ) + j;
  120. }
  121. out->leaf.numLeafSurfaces = LittleLong( in->numSurfaces );
  122. indexes = (int *) Z_Malloc( out->leaf.numLeafSurfaces * 4, TAG_BSP, qfalse);
  123. out->leaf.firstLeafSurface = indexes - cm.leafsurfaces;
  124. for ( j = 0 ; j < out->leaf.numLeafSurfaces ; j++ ) {
  125. indexes[j] = LittleLong( in->firstSurface ) + j;
  126. }
  127. }
  128. }
  129. /*
  130. =================
  131. CMod_LoadNodes
  132. =================
  133. */
  134. void CMod_LoadNodes( lump_t *l, clipMap_t &cm ) {
  135. dnode_t *in;
  136. int child;
  137. cNode_t *out;
  138. int i, j, count;
  139. in = (dnode_t *)(cmod_base + l->fileofs);
  140. if (l->filelen % sizeof(*in))
  141. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  142. count = l->filelen / sizeof(*in);
  143. if (count < 1)
  144. Com_Error (ERR_DROP, "Map has no nodes");
  145. cm.nodes = (cNode_t *) Z_Malloc( count * sizeof( *cm.nodes ), TAG_BSP, qfalse);
  146. cm.numNodes = count;
  147. out = cm.nodes;
  148. for (i=0 ; i<count ; i++, out++, in++)
  149. {
  150. out->plane = cm.planes + LittleLong( in->planeNum );
  151. for (j=0 ; j<2 ; j++)
  152. {
  153. child = LittleLong (in->children[j]);
  154. out->children[j] = child;
  155. }
  156. }
  157. }
  158. /*
  159. =================
  160. CM_BoundBrush
  161. =================
  162. */
  163. void CM_BoundBrush( cbrush_t *b ) {
  164. b->bounds[0][0] = -b->sides[0].plane->dist;
  165. b->bounds[1][0] = b->sides[1].plane->dist;
  166. b->bounds[0][1] = -b->sides[2].plane->dist;
  167. b->bounds[1][1] = b->sides[3].plane->dist;
  168. b->bounds[0][2] = -b->sides[4].plane->dist;
  169. b->bounds[1][2] = b->sides[5].plane->dist;
  170. }
  171. /*
  172. =================
  173. CMod_LoadBrushes
  174. =================
  175. */
  176. void CMod_LoadBrushes( lump_t *l, clipMap_t &cm ) {
  177. dbrush_t *in;
  178. cbrush_t *out;
  179. int i, count;
  180. in = (dbrush_t *)(cmod_base + l->fileofs);
  181. if (l->filelen % sizeof(*in)) {
  182. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  183. }
  184. count = l->filelen / sizeof(*in);
  185. cm.brushes = (cbrush_t *) Z_Malloc( ( BOX_BRUSHES + count ) * sizeof( *cm.brushes ), TAG_BSP, qfalse);
  186. cm.numBrushes = count;
  187. out = cm.brushes;
  188. for ( i=0 ; i<count ; i++, out++, in++ ) {
  189. out->sides = cm.brushsides + LittleLong(in->firstSide);
  190. out->numsides = LittleLong(in->numSides);
  191. out->shaderNum = LittleLong( in->shaderNum );
  192. if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) {
  193. Com_Error( ERR_DROP, "CMod_LoadBrushes: bad shaderNum: %i", out->shaderNum );
  194. }
  195. out->contents = cm.shaders[out->shaderNum].contentFlags;
  196. CM_OrOfAllContentsFlagsInMap |= out->contents;
  197. out->checkcount=0;
  198. CM_BoundBrush( out );
  199. }
  200. }
  201. /*
  202. =================
  203. CMod_LoadLeafs
  204. =================
  205. */
  206. void CMod_LoadLeafs (lump_t *l, clipMap_t &cm)
  207. {
  208. int i;
  209. cLeaf_t *out;
  210. dleaf_t *in;
  211. int count;
  212. in = (dleaf_t *)(cmod_base + l->fileofs);
  213. if (l->filelen % sizeof(*in))
  214. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  215. count = l->filelen / sizeof(*in);
  216. if (count < 1)
  217. Com_Error (ERR_DROP, "Map with no leafs");
  218. cm.leafs = (cLeaf_t *) Z_Malloc( ( BOX_LEAFS + count ) * sizeof( *cm.leafs ), TAG_BSP, qfalse);
  219. cm.numLeafs = count;
  220. out = cm.leafs;
  221. for ( i=0 ; i<count ; i++, in++, out++)
  222. {
  223. out->cluster = LittleLong (in->cluster);
  224. out->area = LittleLong (in->area);
  225. out->firstLeafBrush = LittleLong (in->firstLeafBrush);
  226. out->numLeafBrushes = LittleLong (in->numLeafBrushes);
  227. out->firstLeafSurface = LittleLong (in->firstLeafSurface);
  228. out->numLeafSurfaces = LittleLong (in->numLeafSurfaces);
  229. if (out->cluster >= cm.numClusters)
  230. cm.numClusters = out->cluster + 1;
  231. if (out->area >= cm.numAreas)
  232. cm.numAreas = out->area + 1;
  233. }
  234. cm.areas = (cArea_t *) Z_Malloc( cm.numAreas * sizeof( *cm.areas ), TAG_BSP, qtrue );
  235. cm.areaPortals = (int *) Z_Malloc( cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ), TAG_BSP, qtrue );
  236. }
  237. /*
  238. =================
  239. CMod_LoadPlanes
  240. =================
  241. */
  242. void CMod_LoadPlanes (lump_t *l, clipMap_t &cm)
  243. {
  244. int i, j;
  245. cplane_t *out;
  246. dplane_t *in;
  247. int count;
  248. int bits;
  249. in = (dplane_t *)(cmod_base + l->fileofs);
  250. if (l->filelen % sizeof(*in))
  251. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  252. count = l->filelen / sizeof(*in);
  253. if (count < 1)
  254. Com_Error (ERR_DROP, "Map with no planes");
  255. cm.planes = (struct cplane_s *) Z_Malloc( ( BOX_PLANES + count ) * sizeof( *cm.planes ), TAG_BSP, qfalse);
  256. cm.numPlanes = count;
  257. out = cm.planes;
  258. for ( i=0 ; i<count ; i++, in++, out++)
  259. {
  260. bits = 0;
  261. for (j=0 ; j<3 ; j++)
  262. {
  263. out->normal[j] = LittleFloat (in->normal[j]);
  264. if (out->normal[j] < 0)
  265. bits |= 1<<j;
  266. }
  267. out->dist = LittleFloat (in->dist);
  268. out->type = PlaneTypeForNormal( out->normal );
  269. out->signbits = bits;
  270. }
  271. }
  272. /*
  273. =================
  274. CMod_LoadLeafBrushes
  275. =================
  276. */
  277. void CMod_LoadLeafBrushes (lump_t *l, clipMap_t &cm)
  278. {
  279. int i;
  280. int *out;
  281. int *in;
  282. int count;
  283. in = (int *)(cmod_base + l->fileofs);
  284. if (l->filelen % sizeof(*in))
  285. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  286. count = l->filelen / sizeof(*in);
  287. cm.leafbrushes = (int *) Z_Malloc( ( BOX_BRUSHES + count ) * sizeof( *cm.leafbrushes ), TAG_BSP, qfalse);
  288. cm.numLeafBrushes = count;
  289. out = cm.leafbrushes;
  290. for ( i=0 ; i<count ; i++, in++, out++) {
  291. *out = LittleLong (*in);
  292. }
  293. }
  294. /*
  295. =================
  296. CMod_LoadLeafSurfaces
  297. =================
  298. */
  299. void CMod_LoadLeafSurfaces( lump_t *l, clipMap_t &cm )
  300. {
  301. int i;
  302. int *out;
  303. int *in;
  304. int count;
  305. in = (int *)(cmod_base + l->fileofs);
  306. if (l->filelen % sizeof(*in))
  307. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  308. count = l->filelen / sizeof(*in);
  309. cm.leafsurfaces = (int *) Z_Malloc( count * sizeof( *cm.leafsurfaces ), TAG_BSP, qfalse);
  310. cm.numLeafSurfaces = count;
  311. out = cm.leafsurfaces;
  312. for ( i=0 ; i<count ; i++, in++, out++) {
  313. *out = LittleLong (*in);
  314. }
  315. }
  316. /*
  317. =================
  318. CMod_LoadBrushSides
  319. =================
  320. */
  321. void CMod_LoadBrushSides (lump_t *l, clipMap_t &cm)
  322. {
  323. int i;
  324. cbrushside_t *out;
  325. dbrushside_t *in;
  326. int count;
  327. int num;
  328. in = (dbrushside_t *)(cmod_base + l->fileofs);
  329. if ( l->filelen % sizeof(*in) ) {
  330. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  331. }
  332. count = l->filelen / sizeof(*in);
  333. cm.brushsides = (cbrushside_t *) Z_Malloc( ( BOX_SIDES + count ) * sizeof( *cm.brushsides ), TAG_BSP, qfalse);
  334. cm.numBrushSides = count;
  335. out = cm.brushsides;
  336. for ( i=0 ; i<count ; i++, in++, out++) {
  337. num = LittleLong( in->planeNum );
  338. out->plane = &cm.planes[num];
  339. out->shaderNum = LittleLong( in->shaderNum );
  340. if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) {
  341. Com_Error( ERR_DROP, "CMod_LoadBrushSides: bad shaderNum: %i", out->shaderNum );
  342. }
  343. // out->surfaceFlags = cm.shaders[out->shaderNum].surfaceFlags;
  344. }
  345. }
  346. /*
  347. =================
  348. CMod_LoadEntityString
  349. =================
  350. */
  351. void CMod_LoadEntityString( lump_t *l, clipMap_t &cm ) {
  352. cm.entityString = (char *) Z_Malloc( l->filelen, TAG_BSP, qfalse);
  353. cm.numEntityChars = l->filelen;
  354. memcpy (cm.entityString, cmod_base + l->fileofs, l->filelen);
  355. }
  356. /*
  357. =================
  358. CMod_LoadVisibility
  359. =================
  360. */
  361. #define VIS_HEADER 8
  362. void CMod_LoadVisibility( lump_t *l, clipMap_t &cm ) {
  363. int len;
  364. byte *buf;
  365. len = l->filelen;
  366. if ( !len ) {
  367. cm.clusterBytes = ( cm.numClusters + 31 ) & ~31;
  368. cm.visibility = (unsigned char *) Z_Malloc( cm.clusterBytes, TAG_BSP, qfalse);
  369. memset( cm.visibility, 255, cm.clusterBytes );
  370. return;
  371. }
  372. buf = cmod_base + l->fileofs;
  373. cm.vised = qtrue;
  374. cm.visibility = (unsigned char *) Z_Malloc( len, TAG_BSP, qtrue );
  375. cm.numClusters = LittleLong( ((int *)buf)[0] );
  376. cm.clusterBytes = LittleLong( ((int *)buf)[1] );
  377. memcpy (cm.visibility, buf + VIS_HEADER, len - VIS_HEADER );
  378. }
  379. //==================================================================
  380. /*
  381. =================
  382. CMod_LoadPatches
  383. =================
  384. */
  385. #define MAX_PATCH_VERTS 1024
  386. void CMod_LoadPatches( lump_t *surfs, lump_t *verts, clipMap_t &cm ) {
  387. mapVert_t *dv, *dv_p;
  388. dsurface_t *in;
  389. int count;
  390. int i, j;
  391. int c;
  392. cPatch_t *patch;
  393. vec3_t points[MAX_PATCH_VERTS];
  394. int width, height;
  395. int shaderNum;
  396. in = (dsurface_t *)(cmod_base + surfs->fileofs);
  397. if (surfs->filelen % sizeof(*in))
  398. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  399. cm.numSurfaces = count = surfs->filelen / sizeof(*in);
  400. cm.surfaces = (cPatch_t **) Z_Malloc( cm.numSurfaces * sizeof( cm.surfaces[0] ), TAG_BSP, qtrue );
  401. dv = (mapVert_t *)(cmod_base + verts->fileofs);
  402. if (verts->filelen % sizeof(*dv))
  403. Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size");
  404. // scan through all the surfaces, but only load patches,
  405. // not planar faces
  406. for ( i = 0 ; i < count ; i++, in++ ) {
  407. if ( LittleLong( in->surfaceType ) != MST_PATCH ) {
  408. continue; // ignore other surfaces
  409. }
  410. // FIXME: check for non-colliding patches
  411. cm.surfaces[ i ] = patch = (cPatch_t *) Z_Malloc( sizeof( *patch ), TAG_BSP, qtrue );
  412. // load the full drawverts onto the stack
  413. width = LittleLong( in->patchWidth );
  414. height = LittleLong( in->patchHeight );
  415. c = width * height;
  416. if ( c > MAX_PATCH_VERTS ) {
  417. Com_Error( ERR_DROP, "ParseMesh: MAX_PATCH_VERTS" );
  418. }
  419. dv_p = dv + LittleLong( in->firstVert );
  420. for ( j = 0 ; j < c ; j++, dv_p++ ) {
  421. points[j][0] = LittleFloat( dv_p->xyz[0] );
  422. points[j][1] = LittleFloat( dv_p->xyz[1] );
  423. points[j][2] = LittleFloat( dv_p->xyz[2] );
  424. }
  425. shaderNum = LittleLong( in->shaderNum );
  426. patch->contents = cm.shaders[shaderNum].contentFlags;
  427. CM_OrOfAllContentsFlagsInMap |= patch->contents;
  428. patch->surfaceFlags = cm.shaders[shaderNum].surfaceFlags;
  429. // create the internal facet structure
  430. patch->pc = CM_GeneratePatchCollide( width, height, points );
  431. }
  432. }
  433. //==================================================================
  434. #ifdef BSPC
  435. /*
  436. ==================
  437. CM_FreeMap
  438. Free any loaded map and all submodels
  439. ==================
  440. */
  441. void CM_FreeMap(void) {
  442. memset( &cm, 0, sizeof( cm ) );
  443. Hunk_ClearHigh();
  444. CM_ClearLevelPatches();
  445. }
  446. #endif //BSPC
  447. unsigned CM_LumpChecksum(lump_t *lump) {
  448. return LittleLong (Com_BlockChecksum (cmod_base + lump->fileofs, lump->filelen));
  449. }
  450. unsigned CM_Checksum(dheader_t *header) {
  451. unsigned checksums[16];
  452. checksums[0] = CM_LumpChecksum(&header->lumps[LUMP_SHADERS]);
  453. checksums[1] = CM_LumpChecksum(&header->lumps[LUMP_LEAFS]);
  454. checksums[2] = CM_LumpChecksum(&header->lumps[LUMP_LEAFBRUSHES]);
  455. checksums[3] = CM_LumpChecksum(&header->lumps[LUMP_LEAFSURFACES]);
  456. checksums[4] = CM_LumpChecksum(&header->lumps[LUMP_PLANES]);
  457. checksums[5] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHSIDES]);
  458. checksums[6] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHES]);
  459. checksums[7] = CM_LumpChecksum(&header->lumps[LUMP_MODELS]);
  460. checksums[8] = CM_LumpChecksum(&header->lumps[LUMP_NODES]);
  461. checksums[9] = CM_LumpChecksum(&header->lumps[LUMP_SURFACES]);
  462. checksums[10] = CM_LumpChecksum(&header->lumps[LUMP_DRAWVERTS]);
  463. return LittleLong(Com_BlockChecksum(checksums, 11 * 4));
  464. }
  465. /*
  466. ==================
  467. CM_LoadMap
  468. Loads in the map and all submodels
  469. ==================
  470. */
  471. void *gpvCachedMapDiskImage = NULL;
  472. char gsCachedMapDiskImage[MAX_QPATH];
  473. qboolean gbUsingCachedMapDataRightNow = qfalse; // if true, signifies that you can't delete this at the moment!! (used during z_malloc()-fail recovery attempt)
  474. // called in response to a "devmapbsp blah" or "devmapall blah" command, do NOT use inside CM_Load unless you pass in qtrue
  475. //
  476. // new bool return used to see if anything was freed, used during z_malloc failure re-try
  477. //
  478. qboolean CM_DeleteCachedMap(qboolean bGuaranteedOkToDelete)
  479. {
  480. qboolean bActuallyFreedSomething = qfalse;
  481. if (bGuaranteedOkToDelete || !gbUsingCachedMapDataRightNow)
  482. {
  483. // dump cached disk image...
  484. //
  485. if (gpvCachedMapDiskImage)
  486. {
  487. Z_Free( gpvCachedMapDiskImage );
  488. gpvCachedMapDiskImage = NULL;
  489. bActuallyFreedSomething = qtrue;
  490. }
  491. gsCachedMapDiskImage[0] = '\0';
  492. // force map loader to ignore cached internal BSP structures for next level CM_LoadMap() call...
  493. //
  494. cmg.name[0] = '\0';
  495. }
  496. return bActuallyFreedSomething;
  497. }
  498. static void CM_LoadMap_Actual( const char *name, qboolean clientload, int *checksum, clipMap_t &cm ) {
  499. const int *buf;
  500. int i;
  501. dheader_t header;
  502. static unsigned last_checksum;
  503. void *subBSPData = NULL;
  504. if ( !name || !name[0] ) {
  505. Com_Error( ERR_DROP, "CM_LoadMap: NULL name" );
  506. }
  507. #ifndef BSPC
  508. cm_noAreas = Cvar_Get ("cm_noAreas", "0", CVAR_CHEAT);
  509. cm_noCurves = Cvar_Get ("cm_noCurves", "0", CVAR_CHEAT);
  510. cm_playerCurveClip = Cvar_Get ("cm_playerCurveClip", "1", CVAR_ARCHIVE|CVAR_CHEAT );
  511. #endif
  512. Com_DPrintf( "CM_LoadMap( %s, %i )\n", name, clientload );
  513. if ( !strcmp( cm.name, name ) && clientload ) {
  514. *checksum = last_checksum;
  515. return;
  516. }
  517. if (&cm == &cmg)
  518. {
  519. // if there was a cached disk image but the name was empty (ie ERR_DROP happened) or just doesn't match
  520. // the current name, then ditch it...
  521. //
  522. if (gpvCachedMapDiskImage &&
  523. (gsCachedMapDiskImage[0] == '\0' || strcmp( gsCachedMapDiskImage, name ))
  524. )
  525. {
  526. Z_Free(gpvCachedMapDiskImage);
  527. gpvCachedMapDiskImage = NULL;
  528. gsCachedMapDiskImage[0] = '\0';
  529. CM_ClearMap();
  530. }
  531. }
  532. // if there's a valid map name, and it's the same as last time (respawn?), and it's the server-load,
  533. // then keep the data from last time...
  534. //
  535. if (name[0] && !strcmp( cm.name, name ) && !clientload && &cm == &cmg )
  536. {
  537. // clear some stuff that needs zeroing...
  538. //
  539. cm.floodvalid = 0;
  540. //NO... don't reset this because the brush checkcounts are cached,
  541. //so when you load up, brush checkcounts equal the cm.checkcount
  542. //and the trace will be skipped (because everything loads and
  543. //traces in the same exact order ever time you load the map)
  544. cm.checkcount++;// = 0;
  545. memset(cm.areas, 0, cm.numAreas * sizeof( *cm.areas ));
  546. memset(cm.areaPortals, 0, cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ));
  547. }
  548. else
  549. {
  550. // ... else load map from scratch...
  551. //
  552. if (&cm == &cmg)
  553. {
  554. assert(!clientload); // logic check. I'm assuming that a client load doesn't get this far?
  555. // free old stuff
  556. memset( &cm, 0, sizeof( cm ) );
  557. CM_ClearLevelPatches();
  558. Z_TagFree(TAG_BSP);
  559. if ( !name[0] ) {
  560. cm.numLeafs = 1;
  561. cm.numClusters = 1;
  562. cm.numAreas = 1;
  563. cm.cmodels = (struct cmodel_s *) Z_Malloc( sizeof( *cm.cmodels ), TAG_BSP, qtrue );
  564. *checksum = 0;
  565. return;
  566. }
  567. }
  568. // load the file into a buffer that we either discard as usual at the bottom, or if we've got enough memory
  569. // then keep it long enough to save the renderer re-loading it, then discard it after that.
  570. //
  571. fileHandle_t h;
  572. const int iBSPLen = FS_FOpenFileRead( name, &h, qfalse );
  573. if(!h)
  574. {
  575. Com_Error (ERR_DROP, "Couldn't load %s", name);
  576. return;
  577. }
  578. //rww - only do this when not loading a sub-bsp!
  579. if (&cm == &cmg)
  580. {
  581. if (gpvCachedMapDiskImage && gsCachedMapDiskImage[0])
  582. { //didn't get cleared elsewhere so free it before we allocate the pointer again
  583. //Maps with terrain will allow this to happen because they want everything to be cleared out (going between terrain and no-terrain is messy)
  584. Z_Free(gpvCachedMapDiskImage);
  585. }
  586. gsCachedMapDiskImage[0] = '\0'; // flag that map isn't valid, until name is filled in
  587. gpvCachedMapDiskImage = Z_Malloc( iBSPLen, TAG_BSP_DISKIMAGE, qfalse);
  588. FS_Read(gpvCachedMapDiskImage, iBSPLen, h);
  589. FS_FCloseFile( h );
  590. buf = (int*) gpvCachedMapDiskImage; // so the rest of the code works as normal
  591. }
  592. else
  593. { //otherwise, read straight in..
  594. subBSPData = Z_Malloc( iBSPLen, TAG_BSP_DISKIMAGE, qfalse);
  595. FS_Read(subBSPData, iBSPLen, h);
  596. FS_FCloseFile( h );
  597. buf = (int*)subBSPData;
  598. }
  599. // carry on as before...
  600. last_checksum = LittleLong (Com_BlockChecksum (buf, iBSPLen));
  601. header = *(dheader_t *)buf;
  602. for (i=0 ; i<sizeof(dheader_t)/4 ; i++) {
  603. ((int *)&header)[i] = LittleLong ( ((int *)&header)[i]);
  604. }
  605. if ( header.version != BSP_VERSION )
  606. {
  607. Z_Free( gpvCachedMapDiskImage);
  608. gpvCachedMapDiskImage = NULL;
  609. Com_Error (ERR_DROP, "CM_LoadMap: %s has wrong version number (%i should be %i)"
  610. , name, header.version, BSP_VERSION );
  611. }
  612. cmod_base = (byte *)buf;
  613. // load into heap
  614. CMod_LoadShaders( &header.lumps[LUMP_SHADERS], cm );
  615. CMod_LoadLeafs (&header.lumps[LUMP_LEAFS], cm);
  616. CMod_LoadLeafBrushes (&header.lumps[LUMP_LEAFBRUSHES], cm);
  617. CMod_LoadLeafSurfaces (&header.lumps[LUMP_LEAFSURFACES], cm);
  618. CMod_LoadPlanes (&header.lumps[LUMP_PLANES], cm);
  619. CMod_LoadBrushSides (&header.lumps[LUMP_BRUSHSIDES], cm);
  620. CMod_LoadBrushes (&header.lumps[LUMP_BRUSHES], cm);
  621. CMod_LoadSubmodels (&header.lumps[LUMP_MODELS], cm);
  622. CMod_LoadNodes (&header.lumps[LUMP_NODES], cm);
  623. CMod_LoadEntityString (&header.lumps[LUMP_ENTITIES], cm);
  624. CMod_LoadVisibility( &header.lumps[LUMP_VISIBILITY], cm );
  625. CMod_LoadPatches( &header.lumps[LUMP_SURFACES], &header.lumps[LUMP_DRAWVERTS], cm );
  626. TotalSubModels += cm.numSubModels;
  627. // we are NOT freeing the file, because it is cached for the ref
  628. //actually we do because the new hunk sys won't allow it
  629. // actually we DON'T now <g>, if we've got enough ram to keep it for the renderer's disk-load...
  630. //
  631. extern qboolean Sys_LowPhysicalMemory();
  632. if (Sys_LowPhysicalMemory() //|| com_dedicated->integer // no need to check for dedicated in single-player codebase
  633. )
  634. {
  635. Z_Free( gpvCachedMapDiskImage );
  636. gpvCachedMapDiskImage = NULL;
  637. }
  638. else
  639. {
  640. // ... do nothing, and let the renderer free it after it's finished playing with it...
  641. //
  642. }
  643. if (subBSPData)
  644. {
  645. Z_Free(subBSPData);
  646. }
  647. if (&cm == &cmg)
  648. {
  649. #if !defined(BSPC)
  650. CM_LoadShaderText(false);
  651. // MAT_Init((bool)(!clientload));
  652. #endif
  653. CM_InitBoxHull ();
  654. #if !defined(BSPC)
  655. CM_SetupShaderProperties();
  656. #endif
  657. Q_strncpyz( gsCachedMapDiskImage, name, sizeof(gsCachedMapDiskImage) ); // so the renderer can check it
  658. }
  659. }
  660. *checksum = last_checksum;
  661. // do this whether or not the map was cached from last load...
  662. //
  663. CM_FloodAreaConnections (cm);
  664. // allow this to be cached if it is loaded by the server
  665. if ( !clientload ) {
  666. Q_strncpyz( cm.name, name, sizeof( cm.name ) );
  667. }
  668. CM_CleanLeafCache();
  669. }
  670. // need a wrapper function around this because of multiple returns, need to ensure bool is correct...
  671. //
  672. void CM_LoadMap( const char *name, qboolean clientload, int *checksum, qboolean subBSP )
  673. {
  674. if (subBSP)
  675. {
  676. CM_LoadSubBSP(va("maps/%s.bsp", ((const char *)name) + 1), qfalse);
  677. //CM_LoadMap_Actual( name, clientload, checksum, cmg );
  678. }
  679. else
  680. {
  681. gbUsingCachedMapDataRightNow = qtrue; // !!!!!!!!!!!!!!!!!!
  682. CM_LoadMap_Actual( name, clientload, checksum, cmg );
  683. gbUsingCachedMapDataRightNow = qfalse; // !!!!!!!!!!!!!!!!!!
  684. }
  685. /*
  686. gbUsingCachedMapDataRightNow = qtrue; // !!!!!!!!!!!!!!!!!!
  687. CM_LoadMap_Actual( name, clientload, checksum, cmg );
  688. gbUsingCachedMapDataRightNow = qfalse; // !!!!!!!!!!!!!!!!!!
  689. */
  690. }
  691. qboolean CM_SameMap(char *server)
  692. {
  693. if (!cmg.name[0] || !server || !server[0])
  694. {
  695. return qfalse;
  696. }
  697. if (Q_stricmp(cmg.name, va("maps/%s.bsp", server)))
  698. {
  699. return qfalse;
  700. }
  701. return qtrue;
  702. }
  703. qboolean CM_HasTerrain(void)
  704. {
  705. if (cmg.landScape)
  706. {
  707. return qtrue;
  708. }
  709. return qfalse;
  710. }
  711. /*
  712. ==================
  713. CM_ClearMap
  714. ==================
  715. */
  716. void CM_ClearMap( void )
  717. {
  718. int i;
  719. CM_OrOfAllContentsFlagsInMap = CONTENTS_BODY;
  720. #if !defined(BSPC)
  721. CM_ShutdownShaderProperties();
  722. // MAT_Shutdown();
  723. #endif
  724. if (TheRandomMissionManager)
  725. {
  726. delete TheRandomMissionManager;
  727. TheRandomMissionManager = 0;
  728. }
  729. if (cmg.landScape)
  730. {
  731. delete cmg.landScape;
  732. cmg.landScape = 0;
  733. }
  734. memset( &cmg, 0, sizeof( cmg ) );
  735. CM_ClearLevelPatches();
  736. for(i = 0; i < NumSubBSP; i++)
  737. {
  738. memset(&SubBSP[i], 0, sizeof(SubBSP[0]));
  739. }
  740. NumSubBSP = 0;
  741. TotalSubModels = 0;
  742. }
  743. int CM_TotalMapContents()
  744. {
  745. return CM_OrOfAllContentsFlagsInMap;
  746. }
  747. /*
  748. ==================
  749. CM_ClipHandleToModel
  750. ==================
  751. */
  752. cmodel_t *CM_ClipHandleToModel( clipHandle_t handle, clipMap_t **clipMap )
  753. {
  754. int i;
  755. int count;
  756. if ( handle < 0 )
  757. {
  758. Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i", handle );
  759. }
  760. if ( handle < cmg.numSubModels )
  761. {
  762. if (clipMap)
  763. {
  764. *clipMap = &cmg;
  765. }
  766. return &cmg.cmodels[handle];
  767. }
  768. if ( handle == BOX_MODEL_HANDLE )
  769. {
  770. if (clipMap)
  771. {
  772. *clipMap = &cmg;
  773. }
  774. return &box_model;
  775. }
  776. count = cmg.numSubModels;
  777. for(i = 0; i < NumSubBSP; i++)
  778. {
  779. if (handle < count + SubBSP[i].numSubModels)
  780. {
  781. if (clipMap)
  782. {
  783. *clipMap = &SubBSP[i];
  784. }
  785. return &SubBSP[i].cmodels[handle - count];
  786. }
  787. count += SubBSP[i].numSubModels;
  788. }
  789. if ( handle < MAX_SUBMODELS )
  790. {
  791. Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i < %i < %i",
  792. cmg.numSubModels, handle, MAX_SUBMODELS );
  793. }
  794. Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i", handle + MAX_SUBMODELS );
  795. return NULL;
  796. }
  797. /*
  798. ==================
  799. CM_InlineModel
  800. ==================
  801. */
  802. clipHandle_t CM_InlineModel( int index ) {
  803. if ( index < 0 || index >= TotalSubModels ) {
  804. Com_Error (ERR_DROP, "CM_InlineModel: bad number (may need to re-BSP map?)");
  805. }
  806. return index;
  807. }
  808. int CM_NumClusters( void ) {
  809. return cmg.numClusters;
  810. }
  811. int CM_NumInlineModels( void ) {
  812. return cmg.numSubModels;
  813. }
  814. char *CM_EntityString( void ) {
  815. return cmg.entityString;
  816. }
  817. char *CM_SubBSPEntityString( int index )
  818. {
  819. return SubBSP[index].entityString;
  820. }
  821. int CM_LeafCluster( int leafnum ) {
  822. if (leafnum < 0 || leafnum >= cmg.numLeafs) {
  823. Com_Error (ERR_DROP, "CM_LeafCluster: bad number");
  824. }
  825. return cmg.leafs[leafnum].cluster;
  826. }
  827. int CM_LeafArea( int leafnum ) {
  828. if ( leafnum < 0 || leafnum >= cmg.numLeafs ) {
  829. Com_Error (ERR_DROP, "CM_LeafArea: bad number");
  830. }
  831. return cmg.leafs[leafnum].area;
  832. }
  833. //=======================================================================
  834. /*
  835. ===================
  836. CM_InitBoxHull
  837. Set up the planes and nodes so that the six floats of a bounding box
  838. can just be stored out and get a proper clipping hull structure.
  839. ===================
  840. */
  841. void CM_InitBoxHull (void)
  842. {
  843. int i;
  844. int side;
  845. cplane_t *p;
  846. cbrushside_t *s;
  847. box_planes = &cmg.planes[cmg.numPlanes];
  848. box_brush = &cmg.brushes[cmg.numBrushes];
  849. box_brush->numsides = 6;
  850. box_brush->sides = cmg.brushsides + cmg.numBrushSides;
  851. box_brush->contents = CONTENTS_BODY;
  852. box_model.leaf.numLeafBrushes = 1;
  853. // box_model.leaf.firstLeafBrush = cmg.numBrushes;
  854. box_model.leaf.firstLeafBrush = cmg.numLeafBrushes;
  855. cmg.leafbrushes[cmg.numLeafBrushes] = cmg.numBrushes;
  856. for (i=0 ; i<6 ; i++)
  857. {
  858. side = i&1;
  859. // brush sides
  860. s = &cmg.brushsides[cmg.numBrushSides+i];
  861. s->plane = cmg.planes + (cmg.numPlanes+i*2+side);
  862. s->shaderNum = cmg.numShaders; //not storing flags directly anymore, so be sure to point @ a valid shader
  863. // planes
  864. p = &box_planes[i*2];
  865. p->type = i>>1;
  866. p->signbits = 0;
  867. VectorClear (p->normal);
  868. p->normal[i>>1] = 1;
  869. p = &box_planes[i*2+1];
  870. p->type = 3 + (i>>1);
  871. p->signbits = 0;
  872. VectorClear (p->normal);
  873. p->normal[i>>1] = -1;
  874. SetPlaneSignbits( p );
  875. }
  876. }
  877. /*
  878. ===================
  879. CM_HeadnodeForBox
  880. To keep everything totally uniform, bounding boxes are turned into small
  881. BSP trees instead of being compared directly.
  882. ===================
  883. */
  884. clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs) {//, const int contents ) {
  885. box_planes[0].dist = maxs[0];
  886. box_planes[1].dist = -maxs[0];
  887. box_planes[2].dist = mins[0];
  888. box_planes[3].dist = -mins[0];
  889. box_planes[4].dist = maxs[1];
  890. box_planes[5].dist = -maxs[1];
  891. box_planes[6].dist = mins[1];
  892. box_planes[7].dist = -mins[1];
  893. box_planes[8].dist = maxs[2];
  894. box_planes[9].dist = -maxs[2];
  895. box_planes[10].dist = mins[2];
  896. box_planes[11].dist = -mins[2];
  897. VectorCopy( mins, box_brush->bounds[0] );
  898. VectorCopy( maxs, box_brush->bounds[1] );
  899. //FIXME: this is the "correct" way, but not the way JK2 was designed around... fix for further projects
  900. //box_brush->contents = contents;
  901. return BOX_MODEL_HANDLE;
  902. }
  903. /*
  904. ===================
  905. CM_ModelBounds
  906. ===================
  907. */
  908. void CM_ModelBounds( clipMap_t &cm, clipHandle_t model, vec3_t mins, vec3_t maxs )
  909. {
  910. cmodel_t *cmod;
  911. cmod = CM_ClipHandleToModel( model );
  912. VectorCopy( cmod->mins, mins );
  913. VectorCopy( cmod->maxs, maxs );
  914. }
  915. /*
  916. ===================
  917. CM_RegisterTerrain
  918. Allows physics to examine the terrain data.
  919. ===================
  920. */
  921. #if !defined(BSPC)
  922. CCMLandScape *CM_RegisterTerrain(const char *config, bool server)
  923. {
  924. thandle_t terrainId;
  925. CCMLandScape *ls;
  926. terrainId = atol(Info_ValueForKey(config, "terrainId"));
  927. if(terrainId && cmg.landScape)
  928. {
  929. // Already spawned so just return
  930. ls = cmg.landScape;
  931. ls->IncreaseRefCount();
  932. return(ls);
  933. }
  934. // Doesn't exist so create and link in
  935. //cmg.numTerrains++;
  936. ls = CM_InitTerrain(config, 1, server);
  937. // Increment for the next instance
  938. if (cmg.landScape)
  939. {
  940. Com_Error(ERR_DROP, "You can't have more than one terrain brush.");
  941. }
  942. cmg.landScape = ls;
  943. return(ls);
  944. }
  945. /*
  946. ===================
  947. CM_ShutdownTerrain
  948. ===================
  949. */
  950. void CM_ShutdownTerrain( thandle_t terrainId)
  951. {
  952. CCMLandScape *landscape;
  953. landscape = cmg.landScape;
  954. if (landscape)
  955. {
  956. landscape->DecreaseRefCount();
  957. if(landscape->GetRefCount() <= 0)
  958. {
  959. delete landscape;
  960. cmg.landScape = NULL;
  961. }
  962. }
  963. }
  964. #endif
  965. int CM_LoadSubBSP(const char *name, qboolean clientload)
  966. {
  967. int i;
  968. int checksum;
  969. int count;
  970. count = cmg.numSubModels;
  971. for(i = 0; i < NumSubBSP; i++)
  972. {
  973. if (!stricmp(name, SubBSP[i].name))
  974. {
  975. return count;
  976. }
  977. count += SubBSP[i].numSubModels;
  978. }
  979. if (NumSubBSP == MAX_SUB_BSP)
  980. {
  981. Com_Error (ERR_DROP, "CM_LoadSubBSP: too many unique sub BSPs");
  982. }
  983. CM_LoadMap_Actual( name, clientload, &checksum, SubBSP[NumSubBSP] );
  984. NumSubBSP++;
  985. return count;
  986. }
  987. int CM_FindSubBSP(int modelIndex)
  988. {
  989. int i;
  990. int count;
  991. count = cmg.numSubModels;
  992. if (modelIndex < count)
  993. { // belongs to the main bsp
  994. return -1;
  995. }
  996. for(i = 0; i < NumSubBSP; i++)
  997. {
  998. count += SubBSP[i].numSubModels;
  999. if (modelIndex < count)
  1000. {
  1001. return i;
  1002. }
  1003. }
  1004. return -1;
  1005. }
  1006. void CM_GetWorldBounds ( vec3_t mins, vec3_t maxs )
  1007. {
  1008. VectorCopy ( cmg.cmodels[0].mins, mins );
  1009. VectorCopy ( cmg.cmodels[0].maxs, maxs );
  1010. }
  1011. int CM_ModelContents_Actual( clipHandle_t model, clipMap_t *cm )
  1012. {
  1013. cmodel_t *cmod;
  1014. int contents = 0;
  1015. int i;
  1016. if (!cm)
  1017. {
  1018. cm = &cmg;
  1019. }
  1020. cmod = CM_ClipHandleToModel( model, &cm );
  1021. //MCG ADDED - return the contents, too
  1022. if( cmod->leaf.numLeafBrushes ) // check for brush
  1023. {
  1024. int brushNum;
  1025. for ( i = cmod->leaf.firstLeafBrush; i < cmod->leaf.firstLeafBrush+cmod->leaf.numLeafBrushes; i++ )
  1026. {
  1027. brushNum = cm->leafbrushes[i];
  1028. contents |= cm->brushes[brushNum].contents;
  1029. }
  1030. }
  1031. if( cmod->leaf.numLeafSurfaces ) // if not brush, check for patch
  1032. {
  1033. int surfaceNum;
  1034. for ( i = cmod->leaf.firstLeafSurface; i < cmod->leaf.firstLeafSurface+cmod->leaf.numLeafSurfaces; i++ )
  1035. {
  1036. surfaceNum = cm->leafsurfaces[i];
  1037. if ( cm->surfaces[surfaceNum] != NULL )
  1038. {//HERNH? How could we have a null surf within our cmod->leaf.numLeafSurfaces?
  1039. contents |= cm->surfaces[surfaceNum]->contents;
  1040. }
  1041. }
  1042. }
  1043. return contents;
  1044. }
  1045. int CM_ModelContents( clipHandle_t model, int subBSPIndex )
  1046. {
  1047. if (subBSPIndex < 0)
  1048. {
  1049. return CM_ModelContents_Actual(model, NULL);
  1050. }
  1051. return CM_ModelContents_Actual(model, &SubBSP[subBSPIndex]);
  1052. }
  1053. //support for save/load games
  1054. /*
  1055. ===================
  1056. CM_WritePortalState
  1057. Writes the portal state to a savegame file
  1058. ===================
  1059. */
  1060. //
  1061. qboolean SG_Append(unsigned long chid, const void *data, int length);
  1062. int SG_Read(unsigned long chid, void *pvAddress, int iLength, void **ppvAddressPtr = NULL);
  1063. void CM_WritePortalState ()
  1064. {
  1065. SG_Append('PRTS', (void *)cmg.areaPortals, cmg.numAreas * cmg.numAreas * sizeof( *cmg.areaPortals ));
  1066. }
  1067. /*
  1068. ===================
  1069. CM_ReadPortalState
  1070. Reads the portal state from a savegame file
  1071. and recalculates the area connections
  1072. ===================
  1073. */
  1074. void CM_ReadPortalState ()
  1075. {
  1076. SG_Read('PRTS', (void *)cmg.areaPortals, cmg.numAreas * cmg.numAreas * sizeof( *cmg.areaPortals ));
  1077. CM_FloodAreaConnections (cmg);
  1078. }