OpenSource.c 20 KB

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