OpenSource.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. #include "bsp.h"
  2. #include "cache.h"
  3. #include "collection.h"
  4. #include "mempools.h"
  5. #include "common.h"
  6. #include "texture.h"
  7. #include "profiler.h"
  8. #include "camera.h"
  9. #include "vmfparser.h"
  10. #include "atto/app.h"
  11. #include "atto/math.h"
  12. static char persistent_data[128*1024*1024];
  13. static char temp_data[128*1024*1024];
  14. int willNotPrintDebugMsg = 0;
  15. static struct Stack stack_temp = {
  16. .storage = temp_data,
  17. .size = sizeof(temp_data),
  18. .cursor = 0
  19. };
  20. static struct Stack stack_persistent = {
  21. .storage = persistent_data,
  22. .size = sizeof(persistent_data),
  23. .cursor = 0
  24. };
  25. static struct Memories mem = {
  26. &stack_temp,
  27. &stack_persistent
  28. };
  29. typedef enum {
  30. MapFlags_Empty = 0,
  31. MapFlags_Loaded = 1,
  32. MapFlags_FixedOffset = 2,
  33. MapFlags_Broken = 4
  34. } MapFlags;
  35. typedef struct Map {
  36. char *name;
  37. char *depend_name;
  38. int flags;
  39. struct AVec3f offset;
  40. struct AVec3f debug_offset;
  41. struct BSPModel model;
  42. struct Map *prev, *next;
  43. const struct Map *parent;
  44. struct AVec3f parent_offset;
  45. } Map;
  46. typedef struct Patch {
  47. const char *map_name;
  48. int delete;
  49. BSPLandmark landmark;
  50. struct Patch *next;
  51. } Patch;
  52. static struct {
  53. struct Camera camera;
  54. int forward, right, run;
  55. struct AVec3f center;
  56. float R;
  57. struct ICollection *collection_chain;
  58. Patch *patches;
  59. Map *maps_begin, *maps_end;
  60. int maps_count, maps_limit;
  61. Map *selected_map;
  62. } g;
  63. static Map *opensrcAllocMap(StringView name) {
  64. if (g.maps_count >= g.maps_limit) {
  65. PRINTF("Map limit reached, not trying to add map " PRI_SV, PRI_SVV(name));
  66. return NULL;
  67. }
  68. Map *map = g.maps_begin;
  69. while (map) {
  70. if (strncmp(map->name, name.str, name.length) == 0)
  71. return map;
  72. map = map->next;
  73. }
  74. const int total_size = sizeof(Map) + name.length + 1;
  75. char *buffer = stackAlloc(&stack_persistent, total_size);
  76. if (!buffer) {
  77. PRINT("Not enough memory");
  78. return NULL;
  79. }
  80. memset(buffer, 0, total_size);
  81. map = (void*)buffer;
  82. map->name = buffer + sizeof(Map);
  83. map->depend_name = map->name + name.length + 1;
  84. memcpy(map->name, name.str, name.length);
  85. if (!g.maps_end)
  86. g.maps_begin = map;
  87. else {
  88. g.maps_end->next = map;
  89. map->prev = g.maps_end;
  90. }
  91. g.maps_end = map;
  92. ++g.maps_count;
  93. PRINTF("Added new map to the queue: " PRI_SV, PRI_SVV(name));
  94. return map;
  95. }
  96. void openSourceAddMap(StringView name) {
  97. opensrcAllocMap(name);
  98. }
  99. static void mapUpdatePosition(Map *map) {
  100. if (map->parent && !(map->flags & MapFlags_FixedOffset))
  101. map->offset = aVec3fAdd(map->parent_offset, aVec3fAdd(map->parent->offset, map->parent->debug_offset));
  102. PRINTF("Map %s global_offset %f %f %f", map->name,
  103. map->offset.x, map->offset.y, map->offset.z);
  104. }
  105. static enum BSPLoadResult loadMap(Map *map, ICollection *collection) {
  106. BSPLoadModelContext loadctx = {
  107. .collection = collection,
  108. .persistent = &stack_persistent,
  109. .tmp = &stack_temp,
  110. .model = &map->model,
  111. .name = { .str = map->name, .length = strlen(map->name) },
  112. .prev_map_name = { .str = NULL, .length = 0 },
  113. .next_map_name = { .str = NULL, .length = 0 },
  114. };
  115. if (map->prev) {
  116. loadctx.prev_map_name.str = map->prev->name;
  117. loadctx.prev_map_name.length = strlen(map->prev->name);
  118. }
  119. if (map->next) {
  120. loadctx.next_map_name.str = map->next->name;
  121. loadctx.next_map_name.length = strlen(map->next->name);
  122. }
  123. const enum BSPLoadResult result = bspLoadWorldspawn(loadctx);
  124. if (result != BSPLoadResult_Success) {
  125. PRINTF("Cannot load map \"%s\": %d", map->name, result);
  126. return result;
  127. }
  128. aAppDebugPrintf("Loaded %s to %u draw calls", map->name, map->model.detailed.draws_count);
  129. aAppDebugPrintf("AABB (%f, %f, %f) - (%f, %f, %f)",
  130. map->model.aabb.min.x,
  131. map->model.aabb.min.y,
  132. map->model.aabb.min.z,
  133. map->model.aabb.max.x,
  134. map->model.aabb.max.y,
  135. map->model.aabb.max.z);
  136. for (const Patch *p = g.patches; p; p = p->next) {
  137. if (p->delete || strcasecmp(p->map_name, map->name) != 0)
  138. continue;
  139. int found = 0;
  140. for (int i = 0; i < map->model.landmarks_count; ++i) {
  141. struct BSPLandmark *lm = map->model.landmarks + i;
  142. if (strcasecmp(p->landmark.name, lm->name) == 0) {
  143. found = 1;
  144. break;
  145. }
  146. }
  147. if (found)
  148. continue;
  149. if (map->model.landmarks_count == BSP_MAX_LANDMARKS) {
  150. PRINTF("Too many landmarks for map %s", map->name);
  151. break;
  152. }
  153. PRINTF("Injecting landmark %s %f %f %f to map %s",
  154. p->landmark.name,
  155. p->landmark.origin.x,
  156. p->landmark.origin.y,
  157. p->landmark.origin.z,
  158. map->name);
  159. memmove(map->model.landmarks + 1, map->model.landmarks, sizeof(BSPLandmark) * map->model.landmarks_count);
  160. map->model.landmarks[0] = p->landmark;
  161. ++map->model.landmarks_count;
  162. }
  163. PRINTF("Landmarks: %d", map->model.landmarks_count);
  164. for (int i = 0; i < map->model.landmarks_count; ++i) {
  165. struct BSPLandmark *lm = map->model.landmarks + i;
  166. int deleted = 0;
  167. for (const Patch *p = g.patches; p; p = p->next) {
  168. if (strcasecmp(p->map_name, map->name) == 0 && strcasecmp(p->landmark.name, lm->name) == 0) {
  169. if (p->delete) {
  170. PRINTF("Deleting landmark %s", p->landmark.name);
  171. --map->model.landmarks_count;
  172. memmove(lm, lm + 1, sizeof(BSPLandmark) * (map->model.landmarks_count - i));
  173. deleted = 1;
  174. } else {
  175. PRINTF("Modifying landmark %s %f %f %f -> %f %f %f of map %s",
  176. p->landmark.name,
  177. p->landmark.origin.x,
  178. p->landmark.origin.y,
  179. p->landmark.origin.z,
  180. lm->origin.x,
  181. lm->origin.y,
  182. lm->origin.z,
  183. map->name);
  184. lm->origin = p->landmark.origin;
  185. }
  186. continue;
  187. }
  188. }
  189. if (deleted) {
  190. --i;
  191. continue;
  192. }
  193. PRINTF("\t%d: %s -> (%f, %f, %f)", i + 1, lm->name,
  194. lm->origin.x, lm->origin.y, lm->origin.z);
  195. }
  196. if (map != g.maps_begin && map->model.landmarks_count > 0 && !(map->flags & MapFlags_FixedOffset)) {
  197. for (int k = 0; k < map->model.landmarks_count; ++k) {
  198. const struct BSPLandmark *m1 = map->model.landmarks + k;
  199. for (struct Map *map2 = g.maps_begin; map2; map2 = map2->next) {
  200. if (map2 == map || !(map2->flags & MapFlags_Loaded))
  201. continue;
  202. for (int j = 0; j < map2->model.landmarks_count; ++j) {
  203. const struct BSPLandmark *m2 = map2->model.landmarks + j;
  204. if (strcmp(m1->name, m2->name) == 0) {
  205. map->parent = map2;
  206. map->parent_offset = aVec3fSub(m2->origin, m1->origin);
  207. PRINTF("Map %s parent: %s", map->name, map2->name);
  208. goto loaded;
  209. } // if landmarks match
  210. } // for all landmarks of map 2
  211. } // for all maps (2)
  212. } // for all landmarks of map 1
  213. }
  214. loaded:
  215. map->flags |= MapFlags_Loaded;
  216. mapUpdatePosition(map);
  217. return BSPLoadResult_Success;
  218. }
  219. static void opensrcInit() {
  220. cacheInit(&stack_persistent);
  221. if (!renderInit()) {
  222. PRINT("Failed to initialize render");
  223. aAppTerminate(-1);
  224. }
  225. bspInit();
  226. if (BSPLoadResult_Success != loadMap(g.maps_begin, g.collection_chain))
  227. aAppTerminate(-2);
  228. g.center = aVec3fMulf(aVec3fAdd(g.maps_begin->model.aabb.min, g.maps_begin->model.aabb.max), .5f);
  229. float r = aVec3fLength(aVec3fSub(g.maps_begin->model.aabb.max, g.maps_begin->model.aabb.min)) * .5f;
  230. if (g.R < 10000.f) {
  231. g.R = r * 30.f;
  232. }
  233. aAppDebugPrintf("Center %f, %f, %f, R~=%f", g.center.x, g.center.y, g.center.z, r);
  234. const float t = 0;
  235. cameraLookAt(&g.camera,
  236. aVec3fAdd(g.center, aVec3fMulf(aVec3f(cosf(t*.5f), sinf(t*.5f), .25f), r*.5f)),
  237. g.center, aVec3f(0.f, 0.f, 1.f));
  238. }
  239. static void opensrcResize(ATimeUs timestamp, unsigned int old_w, unsigned int old_h) {
  240. (void)(timestamp); (void)(old_w); (void)(old_h);
  241. renderResize(a_app_state->width, a_app_state->height);
  242. cameraProjection(&g.camera, 1.f, g.R, 3.1415926f/2.f, (float)a_app_state->width / (float)a_app_state->height);
  243. cameraRecompute(&g.camera);
  244. }
  245. static void opensrcPaint(ATimeUs timestamp, float dt) {
  246. (void)(timestamp); (void)(dt);
  247. float move = dt * (g.run?3000.f:300.f);
  248. cameraMove(&g.camera, aVec3f(g.right * move, 0.f, -g.forward * move));
  249. cameraRecompute(&g.camera);
  250. renderBegin();
  251. int triangles = 0;
  252. int can_load_map = 1;
  253. for (struct Map *map = g.maps_begin; map; map = map->next) {
  254. if (map->flags & MapFlags_Broken)
  255. continue;
  256. if (!(map->flags & MapFlags_Loaded)) {
  257. if (can_load_map) {
  258. if (BSPLoadResult_Success != loadMap(map, g.collection_chain))
  259. map->flags |= MapFlags_Broken;
  260. }
  261. can_load_map = 0;
  262. continue;
  263. }
  264. const RDrawParams params = {
  265. .camera = &g.camera,
  266. .translation = aVec3fAdd(map->offset, map->debug_offset),
  267. .selected = map == g.selected_map
  268. };
  269. renderModelDraw(&params, &map->model);
  270. for (int i = 0; i < map->model.detailed.draws_count; ++i)
  271. triangles += map->model.detailed.draws[i].count / 3;
  272. }
  273. renderEnd(&g.camera);
  274. if (profilerFrame(&stack_temp)) {
  275. PRINTF("Total triangles: %d", triangles);
  276. }
  277. }
  278. static void opensrcKeyPress(ATimeUs timestamp, AKey key, int pressed) {
  279. (void)(timestamp); (void)(key); (void)(pressed);
  280. //printf("KEY %u %d %d\n", timestamp, key, pressed);
  281. struct AVec3f map_offset = aVec3ff(0);
  282. int moved_map = 0;
  283. switch (key) {
  284. case AK_Esc:
  285. if (!pressed) break;
  286. if (a_app_state->grabbed)
  287. aAppGrabInput(0);
  288. else
  289. aAppTerminate(0);
  290. break;
  291. case AK_W: g.forward += pressed?1:-1; break;
  292. case AK_S: g.forward += pressed?-1:1; break;
  293. case AK_A: g.right += pressed?-1:1; break;
  294. case AK_D: g.right += pressed?1:-1; break;
  295. case AK_LeftShift: g.run = pressed; break;
  296. default: break;
  297. }
  298. if (pressed) {
  299. switch(key) {
  300. case AK_Up: map_offset.x += 1.f; moved_map = 1; break;
  301. case AK_Down: map_offset.x -= 1.f; moved_map = 1; break;
  302. case AK_Left: map_offset.y -= 1.f; moved_map = 1; break;
  303. case AK_Right: map_offset.y += 1.f; moved_map = 1; break;
  304. case AK_PageUp: map_offset.z += 1.f; moved_map = 1; break;
  305. case AK_PageDown: map_offset.z -= 1.f; moved_map = 1; break;
  306. case AK_Tab:
  307. g.selected_map = g.selected_map ? g.selected_map->next : g.maps_begin;
  308. if (g.selected_map)
  309. PRINTF("Selected map %s", g.selected_map->name);
  310. break;
  311. case AK_Q:
  312. g.selected_map = NULL;
  313. break;
  314. default: break;
  315. }
  316. if (moved_map && g.selected_map) {
  317. Map *map = g.selected_map;
  318. if (g.run) {
  319. map_offset.x *= 100.f;
  320. map_offset.y *= 100.f;
  321. map_offset.z *= 100.f;
  322. }
  323. map->debug_offset = aVec3fAdd(map->debug_offset, map_offset);
  324. PRINTF("Map %s offset: %f %f %f", map->name, map->debug_offset.x, map->debug_offset.y, map->debug_offset.z);
  325. for (Map *m = map; m; m = m->next)
  326. mapUpdatePosition(m);
  327. }
  328. }
  329. }
  330. static void opensrcPointer(ATimeUs timestamp, int dx, int dy, unsigned int btndiff) {
  331. (void)(timestamp); (void)(dx); (void)(dy); (void)(btndiff);
  332. //printf("PTR %u %d %d %x\n", timestamp, dx, dy, btndiff);
  333. if (a_app_state->grabbed) {
  334. cameraRotatePitch(&g.camera, dy * -4e-3f);
  335. cameraRotateYaw(&g.camera, dx * -4e-3f);
  336. cameraRecompute(&g.camera);
  337. } else if (btndiff)
  338. aAppGrabInput(1);
  339. }
  340. static struct ICollection *addToCollectionChain(struct ICollection *chain, struct ICollection *next) {
  341. if (chain) {
  342. struct ICollection *coll = chain;
  343. while (coll->next)
  344. coll = coll->next;
  345. coll->next = next;
  346. return chain;
  347. }
  348. return next;
  349. }
  350. static void opensrcAddLandmarkPatch(StringView map, StringView key, StringView value) {
  351. if (key.length >= BSP_LANDMARK_NAME_LENGTH) {
  352. PRINTF(PRI_SV " is too long", PRI_SVV(key));
  353. return;
  354. }
  355. struct AVec3f origin = aVec3ff(0);
  356. // FIXME sscanf limit by value.length
  357. if (value.length >= 5 && 3 != sscanf(value.str, "%f %f %f", &origin.x, &origin.y, &origin.z)) {
  358. PRINTF(PRI_SV " format is wrong", PRI_SVV(value));
  359. return;
  360. }
  361. Patch *new_patch = stackAlloc(mem.persistent, sizeof(Patch));
  362. new_patch->next = g.patches;
  363. g.patches = new_patch;
  364. char *map_name = stackAlloc(mem.persistent, map.length + 1);
  365. memcpy(map_name, map.str, map.length);
  366. map_name[map.length] = '\0';
  367. new_patch->map_name = map_name;
  368. new_patch->delete = value.length < 5;
  369. memcpy(new_patch->landmark.name, key.str, key.length);
  370. new_patch->landmark.name[key.length] = '\0';
  371. new_patch->landmark.origin = origin;
  372. }
  373. typedef struct {
  374. StringView gamedir;
  375. StringView map_name;
  376. StringView map_offset;
  377. } Config;
  378. const char *steam_basedir = NULL;
  379. static char *buildSteamPath(const StringView *gamedir, const StringView *path) {
  380. const int steam_basedir_length = strlen(steam_basedir);
  381. const int length = steam_basedir_length + gamedir->length + path->length + 4;
  382. char *value = stackAlloc(&stack_temp, length);
  383. if (!value)
  384. return 0;
  385. int offset = 0;
  386. memcpy(value + offset, steam_basedir, steam_basedir_length); offset += steam_basedir_length;
  387. value[offset++] = '/';
  388. memcpy(value + offset, gamedir->str, gamedir->length); offset += gamedir->length;
  389. value[offset++] = '/';
  390. memcpy(value + offset, path->str, path->length); offset += path->length;
  391. value[offset] = '\0';
  392. return value;
  393. }
  394. static VMFAction configPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  395. static VMFAction configReadCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  396. static VMFAction configLandmarkCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  397. Config *cfg = state->user_data;
  398. switch (entry) {
  399. case VMFEntryType_KeyValue:
  400. opensrcAddLandmarkPatch(cfg->map_name, kv->key, kv->value);
  401. break;
  402. case VMFEntryType_SectionClose:
  403. cfg->map_name.length = 0;
  404. state->callback = configPatchCallback;
  405. break;
  406. default:
  407. return VMFAction_SemanticError;
  408. }
  409. return VMFAction_Continue;
  410. }
  411. static VMFAction configMapCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  412. Config *cfg = state->user_data;
  413. switch (entry) {
  414. case VMFEntryType_KeyValue:
  415. if (strncasecmp("name", kv->key.str, kv->key.length) == 0) {
  416. cfg->map_name = kv->value;
  417. } else if (strncasecmp("offset", kv->key.str, kv->key.length) == 0) {
  418. cfg->map_offset = kv->value;
  419. } else {
  420. return VMFAction_SemanticError;
  421. }
  422. break;
  423. case VMFEntryType_SectionClose:
  424. if (cfg->map_name.length < 1)
  425. return VMFAction_SemanticError;
  426. Map *m = opensrcAllocMap(cfg->map_name);
  427. if (m && cfg->map_offset.length >= 5) {
  428. float x, y, z;
  429. // FIXME map_offset is not null-terminated
  430. if (3 == sscanf(cfg->map_offset.str, "%f %f %f", &x, &y, &z)) {
  431. m->flags |= MapFlags_FixedOffset;
  432. m->offset = aVec3f(x, y, z);
  433. } else {
  434. PRINTF("Cannot read offset " PRI_SV, PRI_SVV(cfg->map_offset));
  435. }
  436. }
  437. state->callback = configReadCallback;
  438. break;
  439. default:
  440. return VMFAction_SemanticError;
  441. }
  442. return VMFAction_Continue;
  443. }
  444. static VMFAction configPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  445. Config *cfg = state->user_data;
  446. switch (entry) {
  447. case VMFEntryType_SectionOpen:
  448. if (kv->key.length < 1)
  449. return VMFAction_SemanticError;
  450. cfg->map_name = kv->key;
  451. state->callback = configLandmarkCallback;
  452. break;
  453. case VMFEntryType_SectionClose:
  454. return VMFAction_Exit;
  455. default:
  456. return VMFAction_SemanticError;
  457. }
  458. return VMFAction_Continue;
  459. }
  460. static VMFAction configReadCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  461. Config *cfg = state->user_data;
  462. switch (entry) {
  463. case VMFEntryType_KeyValue:
  464. if (strncasecmp("gamedir", kv->key.str, kv->key.length) == 0) {
  465. cfg->gamedir = kv->value;
  466. } else if (strncasecmp("vpk", kv->key.str, kv->key.length) == 0) {
  467. char *value = buildSteamPath(&cfg->gamedir, &kv->value);
  468. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateVPK(&mem, value));
  469. stackFreeUpToPosition(&stack_temp, value);
  470. } else if (strncasecmp("dir", kv->key.str, kv->key.length) == 0) {
  471. char *value = buildSteamPath(&cfg->gamedir, &kv->value);
  472. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateFilesystem(&mem, value));
  473. stackFreeUpToPosition(&stack_temp, value);
  474. } else if (strncasecmp("max_maps", kv->key.str, kv->key.length) == 0) {
  475. // FIXME null-terminate
  476. g.maps_limit = atoi(kv->value.str);
  477. } else if (strncasecmp("map", kv->key.str, kv->key.length) == 0) {
  478. openSourceAddMap(kv->value);
  479. } else if (strncasecmp("z_far", kv->key.str, kv->key.length) == 0) {
  480. // FIXME null-terminate
  481. g.R = atof(kv->value.str);
  482. } else
  483. return VMFAction_SemanticError;
  484. break;
  485. case VMFEntryType_SectionOpen:
  486. if (strncasecmp("patch_landmarks", kv->key.str, kv->key.length) == 0)
  487. state->callback = configPatchCallback;
  488. else if (strncasecmp("map", kv->key.str, kv->key.length) == 0) {
  489. cfg->map_name.length = cfg->map_offset.length = 0;
  490. state->callback = configMapCallback;
  491. } else
  492. return VMFAction_SemanticError;
  493. break;
  494. default:
  495. return VMFAction_SemanticError;
  496. }
  497. return VMFAction_Continue;
  498. }
  499. static int configReadFile(const char *cfgfile) {
  500. AFile file;
  501. aFileReset(&file);
  502. if (AFile_Success != aFileOpen(&file, cfgfile))
  503. return 0;
  504. char *buffer = stackAlloc(&stack_temp, file.size);
  505. if (!buffer)
  506. return 0;
  507. if (file.size != aFileReadAtOffset(&file, 0, file.size, buffer))
  508. return 0;
  509. Config config = {
  510. .gamedir = { .str = NULL, .length = 0 }
  511. };
  512. VMFState pstate = {
  513. .user_data = &config,
  514. .data = { .str = buffer, .length = file.size },
  515. .callback = configReadCallback
  516. };
  517. aFileClose(&file);
  518. int result = VMFResult_Success == vmfParse(&pstate);
  519. stackFreeUpToPosition(&stack_temp, buffer);
  520. return result;
  521. }
  522. const char *getDefaultSteamBaseDir() {
  523. #ifdef _WIN32
  524. const char *base_dir = getenv("programfiles(x86)");
  525. const char *steam_prefix = "Steam/steamapps/common";
  526. #else // LINUX
  527. const char *base_dir = getenv("HOME");
  528. const char *steam_prefix = ".local/share/Steam/steamapps/common";
  529. #endif
  530. const int base_dir_length = base_dir ? strlen(base_dir) : 0;
  531. const int steam_basedir_length = strlen(steam_prefix) + base_dir_length + 2;
  532. char *steam_basedir_w = stackAlloc(mem.persistent, steam_basedir_length);
  533. sprintf(steam_basedir_w, "%s/%s", base_dir, steam_prefix);
  534. return steam_basedir_w;
  535. }
  536. void attoAppInit(struct AAppProctable *proctable) {
  537. profilerInit();
  538. //aGLInit();
  539. g.collection_chain = NULL;
  540. g.patches = NULL;
  541. g.maps_limit = 1;
  542. g.maps_count = 0;
  543. g.selected_map = NULL;
  544. g.R = 0;
  545. steam_basedir = getDefaultSteamBaseDir();
  546. PRINTF("Steam basedir = %s", steam_basedir);
  547. for (int i = 1; i < a_app_state->argc; ++i) {
  548. const char *argv = a_app_state->argv[i];
  549. if (strcmp(argv, "-s") == 0) {
  550. if (i == a_app_state->argc - 1) {
  551. aAppDebugPrintf("-s requires an argument");
  552. goto print_usage_and_exit;
  553. }
  554. const char *value = a_app_state->argv[++i];
  555. steam_basedir = value;
  556. PRINTF("Steam basedir = %s", steam_basedir);
  557. } else if (strcmp(argv, "-c") == 0) {
  558. if (i == a_app_state->argc - 1) {
  559. aAppDebugPrintf("-c requires an argument");
  560. goto print_usage_and_exit;
  561. }
  562. const char *value = a_app_state->argv[++i];
  563. PRINTF("Reading config file %s", value);
  564. if (!configReadFile(value)) {
  565. PRINTF("Cannot read config file %s", value);
  566. goto print_usage_and_exit;
  567. }
  568. } else if (strcmp(argv, "-p") == 0) {
  569. if (i == a_app_state->argc - 1) {
  570. aAppDebugPrintf("-p requires an argument");
  571. goto print_usage_and_exit;
  572. }
  573. const char *value = a_app_state->argv[++i];
  574. PRINTF("Adding vpk collection at %s", value);
  575. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateVPK(&mem, value));
  576. } else if (strcmp(argv, "-d") == 0) {
  577. if (i == a_app_state->argc - 1) {
  578. aAppDebugPrintf("-d requires an argument");
  579. goto print_usage_and_exit;
  580. }
  581. const char *value = a_app_state->argv[++i];
  582. PRINTF("Adding dir collection at %s", value);
  583. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateFilesystem(&mem, value));
  584. } else if (strcmp(argv, "-n") == 0) {
  585. if (i == a_app_state->argc - 1) {
  586. aAppDebugPrintf("-n requires an argument");
  587. goto print_usage_and_exit;
  588. }
  589. const char *value = a_app_state->argv[++i];
  590. g.maps_limit = atoi(value);
  591. if (g.maps_limit < 1)
  592. g.maps_limit = 1;
  593. } else if (strcmp(argv, "-nodbgprt") == 0) {
  594. willNotPrintDebugMsg = 1;
  595. } else {
  596. const StringView map = { .str = argv, .length = strlen(argv) };
  597. openSourceAddMap(map);
  598. }
  599. }
  600. if (!g.maps_count || !g.collection_chain) {
  601. aAppDebugPrintf("At least one map and one collection required");
  602. goto print_usage_and_exit;
  603. }
  604. opensrcInit();
  605. proctable->resize = opensrcResize;
  606. proctable->paint = opensrcPaint;
  607. proctable->key = opensrcKeyPress;
  608. proctable->pointer = opensrcPointer;
  609. return;
  610. print_usage_and_exit:
  611. aAppDebugPrintf("usage: %s <-c config> <-p vpk> <-d path> [-nodbgprt] ... <mapname0> <mapname1> ...", a_app_state->argv[0]);
  612. aAppTerminate(1);
  613. }