OpenSource.c 19 KB

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