Merge branch 'master' into vfs

This commit is contained in:
Andrei "Akari" Alexeyev 2017-04-21 02:27:05 +03:00
commit 12147f4486
20 changed files with 173 additions and 71 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "cotire"]
path = cotire
url = https://github.com/sakra/cotire

View file

@ -14,6 +14,8 @@ option(RELATIVE "Use only relative paths to the executable and install everythin
option(NO_AUDIO "Build without audio support" OFF)
option(DEBUG_USE_UBSAN "Enable the Undefined Behaviour Sanitizer (UBSan) in debug builds. Only disable if the compiler or target platform doesn't support it." ON)
option(DEBUG_USE_ASAN "Enable the Address Sanitizer (ASan) and leak detection in debug builds." OFF)
option(RELEASE_USE_LTO "Enable Link Time Optimization in release builds." ON)
option(USE_COTIRE "Use cotire (COmpile TIme REducer) to speed up builds" OFF)
add_subdirectory(src)

1
cotire Submodule

@ -0,0 +1 @@
Subproject commit 3d63330c99fde1844e3ad63c98025d18d38019f2

View file

@ -26,7 +26,7 @@ void main(void) {
pos.y *= ratio;
float r = length(pos);
float phi = atan(pos.y,pos.x)+t/10.0;
float rmin = (1.+0.3*sin(t*20.0+10.*phi))*step(0.,t)*t*t;
float rmin = (1.+0.3*sin(t*20.0+10.*phi))*step(0.,t)*t*t*10.0;
gl_FragColor = mix(clr, vec4(1.0) - vec4(clr.rgb,0.), step(r,rmin+t*(.1+0.1*sin(10.*r))));
gl_FragColor.a = float(r > rmin);

View file

@ -1,4 +1,10 @@
if(USE_COTIRE)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cotire/CMake")
cmake_policy(SET CMP0011 NEW)
include(cotire)
endif()
find_package(PkgConfig REQUIRED)
if(LINK_TO_LIBGL)
@ -125,6 +131,7 @@ endif()
message(STATUS "Build configuration: ${CMAKE_BUILD_TYPE}")
set(DEBUG_FLAGS "-ggdb -fno-omit-frame-pointer")
set(RELEASE_FLAGS "-DNDEBUG")
if(DEBUG_USE_UBSAN)
set(DEBUG_FLAGS "${DEBUG_FLAGS} -fsanitize=undefined")
@ -134,8 +141,14 @@ if(DEBUG_USE_ASAN)
set(DEBUG_FLAGS "${DEBUG_FLAGS} -fsanitize=address")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG ${DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${DEBUG_FLAGS}")
if(RELEASE_USE_LTO)
set(RELEASE_FLAGS "${RELEASE_FLAGS} -flto")
endif()
set(CMAKE_C_FLAGS_DEBUG "${DEBUG_FLAGS} -DDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${RELEASE_FLAGS} -O3")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${RELEASE_FLAGS} -O2 ${DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_MINSIZEREL "${RELEASE_FLAGS} -Os")
add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}" -Wall -Wno-parentheses -Wtype-limits -std=c11 -pedantic)
@ -252,3 +265,7 @@ elseif(RELATIVE)
else()
install(TARGETS taisei RUNTIME DESTINATION bin)
endif()
if(USE_COTIRE)
cotire(taisei)
endif()

View file

@ -111,11 +111,11 @@ int color_test(void) {
clr2 = rgba(clra[0], clra[1], clra[2], clra[3]);
clr3 = derive_color(clr1, CLRMASK_A, rgba(0, 0, 0, -1.0));
printf("1: %016llx (%f %f %f %f)\n", (unsigned long long int)clr1,
printf("1: %016"PRIxMAX" (%f %f %f %f)\n", (uintmax_t)clr1,
color_component(clr1, CLR_R), color_component(clr1, CLR_G), color_component(clr1, CLR_B), color_component(clr1, CLR_A));
printf("2: %016llx (%f %f %f %f)\n", (unsigned long long int)clr2,
printf("2: %016"PRIxMAX" (%f %f %f %f)\n", (uintmax_t)clr2,
color_component(clr2, CLR_R), color_component(clr2, CLR_G), color_component(clr2, CLR_B), color_component(clr2, CLR_A));
printf("3: %016llx (%f %f %f %f)\n", (unsigned long long int)clr3,
printf("3: %016"PRIxMAX" (%f %f %f %f)\n", (uintmax_t)clr3,
color_component(clr3, CLR_R), color_component(clr3, CLR_G), color_component(clr3, CLR_B), color_component(clr3, CLR_A));
assert(clr1 == clr2);

View file

@ -253,7 +253,7 @@ static size_t hashtable_find_optimal_size(Hashtable *ht) {
int cols = hashtable_check_collisions_with_new_size(ht, s);
if(cols < col_tolerance) {
log_debug("Optimal size for %p is %llu (%i collisions)", (void*)ht, (unsigned long long)s, cols);
log_debug("Optimal size for %p is %"PRIuMAX" (%i collisions)", (void*)ht, (uintmax_t)s, cols);
return s;
}
@ -263,7 +263,7 @@ static size_t hashtable_find_optimal_size(Hashtable *ht) {
}
}
log_debug("Optimal size for %p is %llu (%i collisions)", (void*)ht, (unsigned long long)best_size, min_cols);
log_debug("Optimal size for %p is %"PRIuMAX" (%i collisions)", (void*)ht, (uintmax_t)best_size, min_cols);
return best_size;
}
@ -287,8 +287,8 @@ static void hashtable_resize_internal(Hashtable *ht, size_t new_size) {
ht->table = new_table;
log_debug("Resized hashtable at %p: %llu -> %llu",
(void*)ht, (unsigned long long)ht->table_size, (unsigned long long)new_size);
log_debug("Resized hashtable at %p: %"PRIuMAX" -> %"PRIuMAX"",
(void*)ht, (uintmax_t)ht->table_size, (uintmax_t)new_size);
ht->table_size = new_size;
}
@ -501,10 +501,10 @@ void hashtable_print_stringkeys(Hashtable *ht) {
log_debug("------ %p:", (void*)ht);
for(size_t i = 0; i < ht->table_size; ++i) {
log_debug("[bucket %lu] %p", (unsigned long)i, (void*)ht->table[i]);
log_debug("[bucket %"PRIuMAX"] %p", (uintmax_t)i, (void*)ht->table[i]);
for(HashtableElement *e = ht->table[i]; e; e = e->next) {
log_debug(" -- %s (%lu): %p", (char*)e->key, (unsigned long)e->hash, e->data);
log_debug(" -- %s (%"PRIuMAX"): %p", (char*)e->key, (uintmax_t)e->hash, e->data);
}
}
@ -526,7 +526,7 @@ void hashtable_print_stringkeys(Hashtable *ht) {
static void hashtable_printstrings(Hashtable *ht) {
for(size_t i = 0; i < ht->table_size; ++i) {
for(HashtableElement *e = ht->table[i]; e; e = e->next) {
log_info("[HT %lu] %s (%lu): %s\n", (unsigned long)i, (char*)e->key, (unsigned long)e->hash, (char*)e->data);
log_info("[HT %"PRIuMAX"] %s (%"PRIuMAX"): %s\n", (uintmax_t)i, (char*)e->key, (uintmax_t)e->hash, (char*)e->data);
}
}
}

View file

@ -62,16 +62,24 @@ void log_backtrace(LogLevel lvl);
LogLevel log_parse_levels(LogLevel lvls, const char *lvlmod);
bool log_initialized(void);
#ifdef __WINDOWS__
// hack to supporess warnings about MS format string extensions, like %I64.
// taisei of course never uses them directly, but MinGW uses them in stdint.h format macros.
#define LOG_PREFIX __extension__
#else
#define LOG_PREFIX
#endif
#ifdef DEBUG
#define log_debug(...) _taisei_log(LOG_DEBUG, false, __func__, __VA_ARGS__)
#define log_debug(...) LOG_PREFIX _taisei_log(LOG_DEBUG, false, __func__, __VA_ARGS__)
#else
#define log_debug(...)
#endif
#define log_info(...) _taisei_log(LOG_INFO, false, __func__, __VA_ARGS__)
#define log_warn(...) _taisei_log(LOG_WARN, false, __func__, __VA_ARGS__)
#define log_fatal(...) _taisei_log_fatal(LOG_FATAL, __func__, __VA_ARGS__)
#define log_custom(lvl, ...) _taisei_log(lvl, false, __func__, __VA_ARGS__)
#define log_info(...) LOG_PREFIX _taisei_log(LOG_INFO, false, __func__, __VA_ARGS__)
#define log_warn(...) LOG_PREFIX _taisei_log(LOG_WARN, false, __func__, __VA_ARGS__)
#define log_fatal(...) LOG_PREFIX _taisei_log_fatal(LOG_FATAL, __func__, __VA_ARGS__)
#define log_custom(lvl, ...) LOG_PREFIX _taisei_log(lvl, false, __func__, __VA_ARGS__)
//
// don't call these directly, use the macros

View file

@ -11,8 +11,7 @@
#include "ingamemenu.h"
#include "global.h"
void continue_game(MenuData *m, void *arg)
{
static void continue_game(MenuData *m, void *arg) {
log_info("The game is being continued...");
if(global.replaymode == REPLAY_RECORD) { // actually... I'd be strange if REPLAY_PLAY ever got there
@ -28,12 +27,10 @@ void continue_game(MenuData *m, void *arg)
delete_projectiles(&global.particles);
}
void give_up(MenuData *m, void *arg) {
static void give_up(MenuData *m, void *arg) {
global.game_over = (MAX_CONTINUES - global.continues)? GAMEOVER_ABORT : GAMEOVER_DEFEAT;
}
void restart_game(MenuData *m, void *arg);
void create_gameover_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;

View file

@ -11,26 +11,56 @@
#include "global.h"
#include "stagedraw.h"
#include "video.h"
#include "options.h"
void return_to_game(MenuData *m, void *arg) {
}
void return_to_title(MenuData *m, void *arg) {
static void return_to_title(MenuData *m, void *arg) {
global.game_over = GAMEOVER_ABORT;
menu_commonaction_close(m, arg);
}
void restart_game(MenuData *m, void *arg) {
global.game_over = GAMEOVER_RESTART;
menu_commonaction_close(m, arg);
}
static void enter_options(MenuData *m, void *arg) {
MenuData opts;
create_options_menu(&opts);
menu_loop(&opts);
}
void create_ingame_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->flags = MF_Abortable | MF_Transient | MF_AlwaysProcessInput;
m->flags = MF_Abortable | MF_AlwaysProcessInput;
m->transition = TransEmpty;
add_menu_entry(m, "Return to Game", return_to_game, NULL);
m->cursor = 1;
m->context = "Game Paused";
add_menu_entry(m, "Options", enter_options, NULL)->transition = TransMenuDark;
add_menu_entry(m, "Return to Game", menu_commonaction_close, NULL);
add_menu_entry(m, "Restart the Game", restart_game, NULL)->transition = TransFadeBlack;
add_menu_entry(m, "Return to Title", return_to_title, NULL)->transition = TransFadeBlack;
add_menu_entry(m, "Stop the Game", return_to_title, NULL)->transition = TransFadeBlack;
set_transition(TransEmpty, 0, m->transition_out_time);
}
static void skip_stage(MenuData *m, void *arg) {
global.game_over = GAMEOVER_WIN;
menu_commonaction_close(m, arg);
}
void create_ingame_menu_replay(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->flags = MF_Abortable | MF_AlwaysProcessInput;
m->transition = TransEmpty;
m->cursor = 1;
m->context = "Replay Paused";
add_menu_entry(m, "Options", enter_options, NULL)->transition = TransMenuDark;
add_menu_entry(m, "Continue Watching", menu_commonaction_close, NULL);
add_menu_entry(m, "Restart the Stage", restart_game, NULL)->transition = TransFadeBlack;
add_menu_entry(m, "Skip the Stage", skip_stage, NULL)->transition = TransFadeBlack;
add_menu_entry(m, "Stop Watching", return_to_title, NULL)->transition = TransFadeBlack;
m->cursor = 1;
set_transition(TransEmpty, 0, m->transition_out_time);
}
@ -79,8 +109,9 @@ void draw_ingame_menu(MenuData *menu) {
}
glColor4f(t-s,t-s,t-s/2, 1-menu_fade(menu));
} else
glColor4f(0.5, 0.5, 0.5, 0.5);
} else {
glColor4f(0.5, 0.5, 0.5, 0.5 * (1-menu_fade(menu)));
}
draw_text(AL_Center, 0, i*35, menu->entries[i].name, _fonts.standard);
}

View file

@ -11,8 +11,11 @@
#include "menu.h"
void draw_ingame_menu_bg(MenuData *menu, float f);
void create_ingame_menu(MenuData *menu);
void draw_ingame_menu(MenuData *menu);
void create_ingame_menu(MenuData *menu);
void create_ingame_menu_replay(MenuData *m);
void restart_game(MenuData *m, void *arg);
#endif

View file

@ -88,7 +88,7 @@ static void progress_read(SDL_RWops *file) {
}
if(filesize > PROGRESS_MAXFILESIZE) {
log_warn("Progress file is huge (%li bytes, %i max)", (long)filesize, PROGRESS_MAXFILESIZE);
log_warn("Progress file is huge (%"PRIi64" bytes, %i max)", filesize, PROGRESS_MAXFILESIZE);
return;
}

View file

@ -241,6 +241,18 @@ void ProjDraw(Projectile *proj, int t) {
glPopMatrix();
}
void ProjDrawNoFlareAdd(Projectile *proj, int t) {
glPushMatrix();
glTranslatef(creal(proj->pos), cimag(proj->pos), 0);
glRotatef(proj->angle*180/M_PI+90, 0, 0, 1);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
_ProjDraw(proj, t);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glPopMatrix();
}
void ProjDrawAdd(Projectile *proj, int t) {
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
ProjDraw(proj, t);

View file

@ -78,6 +78,7 @@ void ProjDraw(Projectile *p, int t);
void _ProjDraw(Projectile *p, int t);
void PartDraw(Projectile *p, int t);
void ProjDrawNoFlareAdd(Projectile *p, int t);
void ProjDrawAdd(Projectile *p, int t);
void ProjDrawSub(Projectile *p, int t);

View file

@ -19,7 +19,6 @@ static uint8_t replay_magic_header[] = REPLAY_MAGIC_HEADER;
void replay_init(Replay *rpy) {
memset(rpy, 0, sizeof(Replay));
stralloc(&rpy->playername, config_get_str(CONFIG_PLAYERNAME));
log_debug("Replay at %p initialized for writing", (void*)rpy);
}
@ -208,7 +207,7 @@ bool replay_write(Replay *rpy, SDL_RWops *file, bool compression) {
vfile = SDL_RWWrapZWriter(abuf, REPLAY_COMPRESSION_CHUNK_SIZE, false);
}
replay_write_string(vfile, rpy->playername);
replay_write_string(vfile, config_get_str(CONFIG_PLAYERNAME));
SDL_WriteLE16(vfile, rpy->numstages);
for(i = 0; i < rpy->numstages; ++i) {
@ -254,7 +253,7 @@ bool replay_write(Replay *rpy, SDL_RWops *file, bool compression) {
}
#ifdef REPLAY_LOAD_GARBAGE_TEST
#define PRINTPROP(prop,fmt) log_debug(#prop " = %" # fmt " [%li / %li]", prop, (long int)SDL_RWtell(file), (long int)filesize)
#define PRINTPROP(prop,fmt) log_debug(#prop " = %" # fmt " [%"PRIi64" / %"PRIi64"]", prop, SDL_RWtell(file), filesize)
#else
#define PRINTPROP(prop,fmt) (void)(prop)
#endif
@ -379,14 +378,14 @@ bool replay_read(Replay *rpy, SDL_RWops *file, ReplayReadMode mode) {
if(filesize < 0) {
log_warn("SDL_RWsize() failed: %s", SDL_GetError());
} else {
log_debug("%li bytes", (long int)filesize);
log_debug("%"PRIi64" bytes", filesize);
}
if(mode & REPLAY_READ_META) {
memset(rpy, 0, sizeof(Replay));
if(filesize > 0 && filesize <= sizeof(replay_magic_header) + 2) {
log_warn("Replay file is too short (%li)", (long int)filesize);
log_warn("Replay file is too short (%"PRIi64")", filesize);
return false;
}
@ -400,7 +399,7 @@ bool replay_read(Replay *rpy, SDL_RWops *file, ReplayReadMode mode) {
if(rpy->version & REPLAY_VERSION_COMPRESSION_BIT) {
if(rpy->fileoffset < SDL_RWtell(file)) {
log_warn("Invalid offset %li", (long int)rpy->fileoffset);
log_warn("Invalid offset %"PRIi32"", rpy->fileoffset);
return false;
}
@ -695,6 +694,10 @@ void replay_play(Replay *rpy, int firstidx) {
break;
}
if(global.game_over == GAMEOVER_RESTART) {
--i;
}
global.game_over = 0;
}

View file

@ -202,7 +202,13 @@ static void stage_start(StageInfo *stage) {
void stage_pause(void) {
MenuData menu;
stop_bgm(false);
create_ingame_menu(&menu);
if(global.replaymode == REPLAY_PLAY) {
create_ingame_menu_replay(&menu);
} else {
create_ingame_menu(&menu);
}
menu_loop(&menu);
resume_bgm();
}

View file

@ -573,8 +573,7 @@ void kurumi_aniwall(Boss *b, int time) {
killall(global.enemies);
}
if(global.diff > D_Easy)
GO_TO(b, VIEWPORT_W/2 + VIEWPORT_W/3*sin(time/200) + I*cimag(b->pos),0.03)
GO_TO(b, VIEWPORT_W/2 + VIEWPORT_W/3*sin(time/200) + I*cimag(b->pos),0.03)
if(time < 0)

View file

@ -33,7 +33,7 @@ AttackInfo stage6_spells[] = {
elly_maxwell, elly_spellbg_classic, BOSS_DEFAULT_GO_POS},
{{ 8, 9, 10, 11}, AT_Spellcard, "Eigenstate ~ Many-World Interpretation", 60, 30000,
elly_eigenstate, elly_spellbg_modern, BOSS_DEFAULT_GO_POS},
{{12, 13, 14, 15}, AT_Spellcard, "Ricci Sign ~ Space Time Curvature", 50, 40000,
{{12, 13, 14, 15}, AT_Spellcard, "Ricci Sign ~ Space Time Curvature", 50, 55000,
elly_ricci, elly_spellbg_modern, BOSS_DEFAULT_GO_POS},
{{16, 17, 18, 19}, AT_Spellcard, "LHC ~ Higgs Boson Uncovered", 60, 50000,
elly_lhc, elly_spellbg_modern, BOSS_DEFAULT_GO_POS},
@ -674,7 +674,7 @@ void elly_unbound(Boss *b, int t) {
global.shake_view = max(0, 16-0.1*(t-120));
}
void set_baryon_rule(EnemyLogicRule r) {
static void set_baryon_rule(EnemyLogicRule r) {
Enemy *e;
for(e = global.enemies; e; e = e->next) {
if(e->draw_rule == Baryon) {
@ -754,40 +754,52 @@ void elly_baryonattack(Boss *b, int t) {
}
int baryon_ricci(Enemy *e, int t) {
if(!creal(e->args[2]) || creal(e->args[2]) > 2) {
GO_TO(e, creal(global.boss->pos) + creal(e->pos0-global.boss->pos)*1.5+40.0*I, 0.02);
int num = creal(e->args[2])+0.5;
if(num % 2 == 0) {
GO_TO(e, global.boss->pos,0.03);
} else {
TIMER(&t);
FROM_TO(200, 400, 1) {
GO_TO(e, creal(global.boss->pos) + creal(e->pos0-global.boss->pos)*1.8 + 230.0*I, 0.05);
}
GO_TO(e, global.boss->pos + 200*cexp(I*2*M_PI*(1./6*creal(e->args[2]))+0.001*I*t), 0.03);
FROM_TO_INT(300, 10000, 100, 200, 1)
GO_TO(e, creal(global.boss->pos) + creal(e->pos0-global.boss->pos)*1.8 + 230.0*I + 200.0*I*(_i&1), 0.02);
/*FROM_TO_INT(300, 10000, 100, 200, 1)
GO_TO(e, creal(global.boss->pos) + creal(e->pos0-global.boss->pos)*1.8 + 230.0*I + 200.0*I*(_i&1), 0.02);*/
}
return 1;
}
static double waveform(double x) {
if(x < 0)
return 0;
return 1*sin(1*x)*(exp(10*(sin(x)-0.9)));
}
int ricci_proj(Projectile *p, int t) {
if(t < 0)
return 1;
p->pos += p->args[0];
if(t > creal(p->args[1])) {
Enemy *e;
for(e = global.enemies; e; e = e->next) {
if(cimag(e->pos) < 200)
continue;
int time = global.frames-global.boss->current->starttime;
float c = 3;
float f = max(20,cabs(e->pos-p->pos));
p->args[0] += 70*pow(f, -3)*(e->pos-p->pos);
complex shift = 0;
Enemy *e;
int i = 0;
p->pos = p->pos0 + p->args[0]*t;
for(e = global.enemies; e; e = e->next, i++) {
complex d = e->pos-p->pos;
int starttime = 0+70*i;
if((int)(creal(e->args[2])+0.5) & 1) {
shift += waveform(0.005*(-cabs(d)+c*(time-starttime)))*I*d/cabs(d);
}
}
p->pos = p->pos0 + p->args[0]*t+10*shift;
p->angle = carg(p->args[0]);
p->clr = derive_color(p->clr, CLRMASK_B, rgb(0, 0, cabs(p->args[0])*0.5));
float r,g,b,a;
parse_color(p->clr,&r,&g,&b, &a);
p->clr = rgb(r,g,cabs(shift));
return 1;
}
@ -799,11 +811,18 @@ void elly_ricci(Boss *b, int t) {
AT(EVENT_DEATH)
set_baryon_rule(baryon_reset);
FROM_TO(80, 100000, 50-global.diff) {
FROM_TO(80, 100000, 20) {
int i;
int c = 6 + global.diff;
for(i = 0; i < c*2; i++)
create_projectile2c("plainball", fmod(VIEWPORT_W/(float)c*(i/2),VIEWPORT_W)+VIEWPORT_H*I*(i&1), rgb(0.3+0.7*(i&1), 1-0.7*(i&1), 0), ricci_proj, I-2.0*I*(i&1), 200-t)->draw = ProjDrawAdd;
int c = 9 + global.diff;
bool left = (_i/(20/(1+_i/20)))%2;
for(i = 0; i < c*2; i++) {
complex pos = fmod(VIEWPORT_W/(float)c*(i/2),VIEWPORT_W)+VIEWPORT_H*I*(i&1);
if(left)
pos = fmod(VIEWPORT_H/(float)c*(i/2),VIEWPORT_H)*I+VIEWPORT_W*(i&1);
Projectile *p = create_projectile2c("flea", pos, rgb(0.3+0.7*(i&1), 1-0.7*(i&1), 0), ricci_proj, 2.*(I-2.0*I*(i&1))*(left ? -I : 1), 200-t);
p->draw = ProjDrawNoFlareAdd;
}
}
}

View file

@ -343,14 +343,13 @@ char* read_all(const char *filename, int *outsize) {
}
bool parse_keyvalue_stream_cb(SDL_RWops *strm, KVCallback callback, void *data) {
static const size_t bufsize = 256;
static const char separator[] = "= ";
char buffer[bufsize];
char buffer[256];
int lineno = 0;
int errors = 0;
loopstart: while(SDL_RWgets(strm, buffer, bufsize)) {
loopstart: while(SDL_RWgets(strm, buffer, sizeof(buffer))) {
char *ptr = buffer;
char *sep, *key, *val;

View file

@ -19,6 +19,7 @@
#ifndef __GNUC__ // clang defines this too
#define __attribute__(...)
#define __extension__
#endif
//