OpenSource.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. g.R = aVec3fLength(aVec3fSub(g.maps_begin->model.aabb.max, g.maps_begin->model.aabb.min)) * .5f;
  216. aAppDebugPrintf("Center %f, %f, %f, R~=%f", g.center.x, g.center.y, g.center.z, g.R);
  217. const float t = 0;
  218. cameraLookAt(&g.camera,
  219. aVec3fAdd(g.center, aVec3fMulf(aVec3f(cosf(t*.5f), sinf(t*.5f), .25f), g.R*.5f)),
  220. g.center, aVec3f(0.f, 0.f, 1.f));
  221. }
  222. static void opensrcResize(ATimeUs timestamp, unsigned int old_w, unsigned int old_h) {
  223. (void)(timestamp); (void)(old_w); (void)(old_h);
  224. renderResize(a_app_state->width, a_app_state->height);
  225. cameraProjection(&g.camera, 1.f, g.R * 20.f, 3.1415926f/2.f, (float)a_app_state->width / (float)a_app_state->height);
  226. cameraRecompute(&g.camera);
  227. }
  228. static void opensrcPaint(ATimeUs timestamp, float dt) {
  229. (void)(timestamp); (void)(dt);
  230. float move = dt * (g.run?3000.f:300.f);
  231. cameraMove(&g.camera, aVec3f(g.right * move, 0.f, -g.forward * move));
  232. cameraRecompute(&g.camera);
  233. renderBegin();
  234. int triangles = 0;
  235. int can_load_map = 1;
  236. for (struct Map *map = g.maps_begin; map; map = map->next) {
  237. if (map->flags & MapFlags_Broken)
  238. continue;
  239. if (!(map->flags & MapFlags_Loaded)) {
  240. if (can_load_map) {
  241. if (BSPLoadResult_Success != loadMap(map, g.collection_chain))
  242. map->flags |= MapFlags_Broken;
  243. }
  244. can_load_map = 0;
  245. continue;
  246. }
  247. const RDrawParams params = {
  248. .camera = &g.camera,
  249. .translation = aVec3fAdd(map->offset, map->debug_offset),
  250. .selected = map == g.selected_map
  251. };
  252. renderModelDraw(&params, &map->model);
  253. for (int i = 0; i < map->model.detailed.draws_count; ++i)
  254. triangles += map->model.detailed.draws[i].count / 3;
  255. }
  256. renderEnd(&g.camera);
  257. if (profilerFrame(&stack_temp)) {
  258. PRINTF("Total triangles: %d", triangles);
  259. }
  260. }
  261. static void opensrcKeyPress(ATimeUs timestamp, AKey key, int pressed) {
  262. (void)(timestamp); (void)(key); (void)(pressed);
  263. //printf("KEY %u %d %d\n", timestamp, key, pressed);
  264. struct AVec3f map_offset = aVec3ff(0);
  265. int moved_map = 0;
  266. switch (key) {
  267. case AK_Esc:
  268. if (!pressed) break;
  269. if (a_app_state->grabbed)
  270. aAppGrabInput(0);
  271. else
  272. aAppTerminate(0);
  273. break;
  274. case AK_W: g.forward += pressed?1:-1; break;
  275. case AK_S: g.forward += pressed?-1:1; break;
  276. case AK_A: g.right += pressed?-1:1; break;
  277. case AK_D: g.right += pressed?1:-1; break;
  278. case AK_LeftShift: g.run = pressed; break;
  279. default: break;
  280. }
  281. if (pressed) {
  282. switch(key) {
  283. case AK_Up: map_offset.x += 1.f; moved_map = 1; break;
  284. case AK_Down: map_offset.x -= 1.f; moved_map = 1; break;
  285. case AK_Left: map_offset.y -= 1.f; moved_map = 1; break;
  286. case AK_Right: map_offset.y += 1.f; moved_map = 1; break;
  287. case AK_PageUp: map_offset.z += 1.f; moved_map = 1; break;
  288. case AK_PageDown: map_offset.z -= 1.f; moved_map = 1; break;
  289. case AK_Tab:
  290. g.selected_map = g.selected_map ? g.selected_map->next : g.maps_begin;
  291. if (g.selected_map)
  292. PRINTF("Selected map %s", g.selected_map->name);
  293. break;
  294. case AK_Q:
  295. g.selected_map = NULL;
  296. break;
  297. default: break;
  298. }
  299. if (moved_map && g.selected_map) {
  300. Map *map = g.selected_map;
  301. if (g.run) {
  302. map_offset.x *= 100.f;
  303. map_offset.y *= 100.f;
  304. map_offset.z *= 100.f;
  305. }
  306. map->debug_offset = aVec3fAdd(map->debug_offset, map_offset);
  307. PRINTF("Map %s offset: %f %f %f", map->name, map->debug_offset.x, map->debug_offset.y, map->debug_offset.z);
  308. for (Map *m = map; m; m = m->next)
  309. mapUpdatePosition(m);
  310. }
  311. }
  312. }
  313. static void opensrcPointer(ATimeUs timestamp, int dx, int dy, unsigned int btndiff) {
  314. (void)(timestamp); (void)(dx); (void)(dy); (void)(btndiff);
  315. //printf("PTR %u %d %d %x\n", timestamp, dx, dy, btndiff);
  316. if (a_app_state->grabbed) {
  317. cameraRotatePitch(&g.camera, dy * -4e-3f);
  318. cameraRotateYaw(&g.camera, dx * -4e-3f);
  319. cameraRecompute(&g.camera);
  320. } else if (btndiff)
  321. aAppGrabInput(1);
  322. }
  323. static struct ICollection *addToCollectionChain(struct ICollection *chain, struct ICollection *next) {
  324. if (chain) {
  325. struct ICollection *coll = chain;
  326. while (coll->next)
  327. coll = coll->next;
  328. coll->next = next;
  329. return chain;
  330. }
  331. return next;
  332. }
  333. static void opensrcAddLandmarkPatch(StringView map, StringView key, StringView value) {
  334. if (key.length >= BSP_LANDMARK_NAME_LENGTH) {
  335. PRINTF(PRI_SV " is too long", PRI_SVV(key));
  336. return;
  337. }
  338. struct AVec3f origin = aVec3ff(0);
  339. // FIXME sscanf limit by value.length
  340. if (value.length >= 5 && 3 != sscanf(value.str, "%f %f %f", &origin.x, &origin.y, &origin.z)) {
  341. PRINTF(PRI_SV " format is wrong", PRI_SVV(value));
  342. return;
  343. }
  344. Patch *new_patch = stackAlloc(mem.persistent, sizeof(Patch));
  345. new_patch->next = g.patches;
  346. g.patches = new_patch;
  347. char *map_name = stackAlloc(mem.persistent, map.length + 1);
  348. memcpy(map_name, map.str, map.length);
  349. map_name[map.length] = '\0';
  350. new_patch->map_name = map_name;
  351. new_patch->delete = value.length < 5;
  352. memcpy(new_patch->landmark.name, key.str, key.length);
  353. new_patch->landmark.name[key.length] = '\0';
  354. new_patch->landmark.origin = origin;
  355. }
  356. typedef struct {
  357. StringView gamedir;
  358. StringView map_name;
  359. StringView map_offset;
  360. } Config;
  361. static char *buildSteamPath(const StringView *gamedir, const StringView *path) {
  362. // FIXME windows and macos paths?
  363. const char *home_dir = getenv("HOME");
  364. const int home_dir_length = strlen(home_dir);
  365. const char steam_basedir[] = ".local/share/Steam/steamapps/common";
  366. const int steam_basedir_length = sizeof(steam_basedir) - 1;
  367. const int length = home_dir_length + steam_basedir_length + gamedir->length + path->length + 4;
  368. char *value = stackAlloc(&stack_temp, length);
  369. if (!value)
  370. return 0;
  371. int offset = 0;
  372. memcpy(value + offset, home_dir, home_dir_length); offset += home_dir_length;
  373. value[offset++] = '/';
  374. memcpy(value + offset, steam_basedir, steam_basedir_length); offset += steam_basedir_length;
  375. value[offset++] = '/';
  376. memcpy(value + offset, gamedir->str, gamedir->length); offset += gamedir->length;
  377. value[offset++] = '/';
  378. memcpy(value + offset, path->str, path->length); offset += path->length;
  379. value[offset] = '\0';
  380. return value;
  381. }
  382. static VMFAction configPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  383. static VMFAction configReadCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv);
  384. static VMFAction configLandmarkCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  385. Config *cfg = state->user_data;
  386. switch (entry) {
  387. case VMFEntryType_KeyValue:
  388. opensrcAddLandmarkPatch(cfg->map_name, kv->key, kv->value);
  389. break;
  390. case VMFEntryType_SectionClose:
  391. cfg->map_name.length = 0;
  392. state->callback = configPatchCallback;
  393. break;
  394. default:
  395. return VMFAction_SemanticError;
  396. }
  397. return VMFAction_Continue;
  398. }
  399. static VMFAction configMapCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  400. Config *cfg = state->user_data;
  401. switch (entry) {
  402. case VMFEntryType_KeyValue:
  403. if (strncasecmp("name", kv->key.str, kv->key.length) == 0) {
  404. cfg->map_name = kv->value;
  405. } else if (strncasecmp("offset", kv->key.str, kv->key.length) == 0) {
  406. cfg->map_offset = kv->value;
  407. } else {
  408. return VMFAction_SemanticError;
  409. }
  410. break;
  411. case VMFEntryType_SectionClose:
  412. if (cfg->map_name.length < 1)
  413. return VMFAction_SemanticError;
  414. Map *m = opensrcAllocMap(cfg->map_name);
  415. if (m && cfg->map_offset.length >= 5) {
  416. float x, y, z;
  417. // FIXME map_offset is not null-terminated
  418. if (3 == sscanf(cfg->map_offset.str, "%f %f %f", &x, &y, &z)) {
  419. m->flags |= MapFlags_FixedOffset;
  420. m->offset = aVec3f(x, y, z);
  421. } else {
  422. PRINTF("Cannot read offset " PRI_SV, PRI_SVV(cfg->map_offset));
  423. }
  424. }
  425. state->callback = configReadCallback;
  426. break;
  427. default:
  428. return VMFAction_SemanticError;
  429. }
  430. return VMFAction_Continue;
  431. }
  432. static VMFAction configPatchCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  433. Config *cfg = state->user_data;
  434. switch (entry) {
  435. case VMFEntryType_SectionOpen:
  436. if (kv->key.length < 1)
  437. return VMFAction_SemanticError;
  438. cfg->map_name = kv->key;
  439. state->callback = configLandmarkCallback;
  440. break;
  441. case VMFEntryType_SectionClose:
  442. return VMFAction_Exit;
  443. default:
  444. return VMFAction_SemanticError;
  445. }
  446. return VMFAction_Continue;
  447. }
  448. static VMFAction configReadCallback(VMFState *state, VMFEntryType entry, const VMFKeyValue *kv) {
  449. Config *cfg = state->user_data;
  450. switch (entry) {
  451. case VMFEntryType_KeyValue:
  452. if (strncasecmp("gamedir", kv->key.str, kv->key.length) == 0) {
  453. cfg->gamedir = kv->value;
  454. } else if (strncasecmp("vpk", kv->key.str, kv->key.length) == 0) {
  455. char *value = buildSteamPath(&cfg->gamedir, &kv->value);
  456. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateVPK(&mem, value));
  457. stackFreeUpToPosition(&stack_temp, value);
  458. } else if (strncasecmp("dir", kv->key.str, kv->key.length) == 0) {
  459. char *value = buildSteamPath(&cfg->gamedir, &kv->value);
  460. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateFilesystem(&mem, value));
  461. stackFreeUpToPosition(&stack_temp, value);
  462. } else if (strncasecmp("max_maps", kv->key.str, kv->key.length) == 0) {
  463. // FIXME null-terminate
  464. g.maps_limit = atoi(kv->value.str);
  465. } else if (strncasecmp("map", kv->key.str, kv->key.length) == 0) {
  466. openSourceAddMap(kv->value);
  467. }
  468. break;
  469. case VMFEntryType_SectionOpen:
  470. if (strncasecmp("patch_landmarks", kv->key.str, kv->key.length) == 0)
  471. state->callback = configPatchCallback;
  472. else if (strncasecmp("map", kv->key.str, kv->key.length) == 0) {
  473. cfg->map_name.length = cfg->map_offset.length = 0;
  474. state->callback = configMapCallback;
  475. } else
  476. return VMFAction_SemanticError;
  477. break;
  478. default:
  479. return VMFAction_SemanticError;
  480. }
  481. return VMFAction_Continue;
  482. }
  483. static int configReadFile(const char *cfgfile) {
  484. AFile file;
  485. aFileReset(&file);
  486. if (AFile_Success != aFileOpen(&file, cfgfile))
  487. return 0;
  488. char *buffer = stackAlloc(&stack_temp, file.size);
  489. if (!buffer)
  490. return 0;
  491. if (file.size != aFileReadAtOffset(&file, 0, file.size, buffer))
  492. return 0;
  493. Config config = {
  494. .gamedir = { .str = NULL, .length = 0 }
  495. };
  496. VMFState pstate = {
  497. .user_data = &config,
  498. .data = { .str = buffer, .length = file.size },
  499. .callback = configReadCallback
  500. };
  501. aFileClose(&file);
  502. int result = VMFResult_Success == vmfParse(&pstate);
  503. stackFreeUpToPosition(&stack_temp, buffer);
  504. return result;
  505. }
  506. void attoAppInit(struct AAppProctable *proctable) {
  507. profilerInit();
  508. //aGLInit();
  509. g.collection_chain = NULL;
  510. g.patches = NULL;
  511. g.maps_limit = 1;
  512. g.maps_count = 0;
  513. g.selected_map = NULL;
  514. for (int i = 1; i < a_app_state->argc; ++i) {
  515. const char *argv = a_app_state->argv[i];
  516. if (strcmp(argv, "-c") == 0) {
  517. if (i == a_app_state->argc - 1) {
  518. aAppDebugPrintf("-c requires an argument");
  519. goto print_usage_and_exit;
  520. }
  521. const char *value = a_app_state->argv[++i];
  522. PRINTF("Reading config file %s", value);
  523. if (!configReadFile(value)) {
  524. PRINTF("Cannot read config file %s", value);
  525. goto print_usage_and_exit;
  526. }
  527. } else if (strcmp(argv, "-p") == 0) {
  528. if (i == a_app_state->argc - 1) {
  529. aAppDebugPrintf("-p requires an argument");
  530. goto print_usage_and_exit;
  531. }
  532. const char *value = a_app_state->argv[++i];
  533. PRINTF("Adding vpk collection at %s", value);
  534. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateVPK(&mem, value));
  535. } else if (strcmp(argv, "-d") == 0) {
  536. if (i == a_app_state->argc - 1) {
  537. aAppDebugPrintf("-d requires an argument");
  538. goto print_usage_and_exit;
  539. }
  540. const char *value = a_app_state->argv[++i];
  541. PRINTF("Adding dir collection at %s", value);
  542. g.collection_chain = addToCollectionChain(g.collection_chain, collectionCreateFilesystem(&mem, value));
  543. } else if (strcmp(argv, "-n") == 0) {
  544. if (i == a_app_state->argc - 1) {
  545. aAppDebugPrintf("-p requires an argument");
  546. goto print_usage_and_exit;
  547. }
  548. const char *value = a_app_state->argv[++i];
  549. g.maps_limit = atoi(value);
  550. if (g.maps_limit < 1)
  551. g.maps_limit = 1;
  552. } else {
  553. const StringView map = { .str = argv, .length = strlen(argv) };
  554. openSourceAddMap(map);
  555. }
  556. }
  557. if (!g.maps_count || !g.collection_chain) {
  558. aAppDebugPrintf("At least one map and one collection required");
  559. goto print_usage_and_exit;
  560. }
  561. opensrcInit();
  562. proctable->resize = opensrcResize;
  563. proctable->paint = opensrcPaint;
  564. proctable->key = opensrcKeyPress;
  565. proctable->pointer = opensrcPointer;
  566. return;
  567. print_usage_and_exit:
  568. aAppDebugPrintf("usage: %s <-c config> <-p vpk> <-d path> ... <mapname0> <mapname1> ...", a_app_state->argv[0]);
  569. aAppTerminate(1);
  570. }