Index: src/Cube.mm ================================================================== --- src/Cube.mm +++ src/Cube.mm @@ -7,11 +7,10 @@ VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100); VARP(minmillis, 0, 5, 1000); @implementation Cube { int _width, _height; - OFIRI *_userDataIRI; } + (Cube *)sharedInstance { return (Cube *)OFApplication.sharedApplication.delegate; Index: src/client.mm ================================================================== --- src/client.mm +++ src/client.mm @@ -75,13 +75,14 @@ } } COMMANDN(team, newteam, ARG_1STR) void -writeclientinfo(FILE *f) +writeclientinfo(OFStream *stream) { - fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team); + [stream writeFormat:@"name \"%s\"\nteam \"%s\"\n", player1->name, + player1->team]; } void connects(OFString *servername) { Index: src/command.mm ================================================================== --- src/command.mm +++ src/command.mm @@ -583,38 +583,51 @@ } void writecfg() { - FILE *f = fopen("config.cfg", "w"); - if (!f) + OFStream *stream; + @try { + OFIRI *IRI = [Cube.sharedInstance.userDataIRI + IRIByAppendingPathComponent:@"config.cfg"]; + stream = [[OFIRIHandler handlerForIRI:IRI] openItemAtIRI:IRI + mode:@"w"]; + } @catch (id e) { return; - fprintf(f, "// automatically written on exit, do not modify\n// delete " - "this file to have defaults.cfg overwrite these " - "settings\n// modify settings in game, or put settings in " - "autoexec.cfg to override anything\n\n"); - writeclientinfo(f); - fprintf(f, "\n"); + } + + [stream writeString: + @"// automatically written on exit, do not modify\n" + @"// delete this file to have defaults.cfg overwrite these " + @"settings\n" + @"// modify settings in game, or put settings in " + @"autoexec.cfg to override anything\n" + @"\n"]; + writeclientinfo(stream); + [stream writeString:@"\n"]; + [idents enumerateKeysAndObjectsUsingBlock:^( OFString *name, Ident *ident, bool *stop) { if (ident.type == ID_VAR && ident.persist) { - fprintf(f, "%s %d\n", ident.name.UTF8String, - *ident.storage); + [stream + writeFormat:@"%@ %d\n", ident.name, *ident.storage]; } }]; - fprintf(f, "\n"); - writebinds(f); - fprintf(f, "\n"); + [stream writeString:@"\n"]; + + writebinds(stream); + [stream writeString:@"\n"]; + [idents enumerateKeysAndObjectsUsingBlock:^( OFString *name, Ident *ident, bool *stop) { if (ident.type == ID_ALIAS && - !strstr(ident.name.UTF8String, "nextmap_")) { - fprintf(f, "alias \"%s\" [%s]\n", ident.name.UTF8String, - ident.action.UTF8String); - } + ![ident.name hasPrefix:@"nextmap_"]) + [stream writeFormat:@"alias \"%@\" [%@]\n", ident.name, + ident.action]; }]; - fclose(f); + + [stream close]; } COMMAND(writecfg, ARG_NONE) // below the commands that implement a small imperative language. thanks to the Index: src/console.mm ================================================================== --- src/console.mm +++ src/console.mm @@ -91,42 +91,57 @@ }; }; // keymap is defined externally in keymap.cfg -struct keym { - int code; - char *name; - char *action; -} keyms[256]; -int numkm = 0; +@interface KeyMapping : OFObject +@property (readonly) int code; +@property (readonly, nonatomic) OFString *name; +@property (copy, nonatomic) OFString *action; + +- (instancetype)initWithCode:(int)code name:(OFString *)name; +@end + +@implementation KeyMapping +- (instancetype)initWithCode:(int)code name:(OFString *)name +{ + self = [super init]; + + _code = code; + _name = [name copy]; + + return self; +} +@end + +static OFMutableArray *keyMappings = nil; void keymap(OFString *code, OFString *key, OFString *action) { - @autoreleasepool { - keyms[numkm].code = (int)code.longLongValue; - keyms[numkm].name = newstring(key.UTF8String); - keyms[numkm++].action = newstringbuf(action.UTF8String); - } + if (keyMappings == nil) + keyMappings = [[OFMutableArray alloc] init]; + + KeyMapping *mapping = + [[KeyMapping alloc] initWithCode:(int)code.longLongValue name:key]; + mapping.action = action; + [keyMappings addObject:mapping]; } COMMAND(keymap, ARG_3STR) void -bindkey(OFString *key_, OFString *action) -{ - @autoreleasepool { - std::unique_ptr key(strdup(key_.UTF8String)); - for (char *x = key.get(); *x; x++) - *x = toupper(*x); - loopi(numkm) if (strcmp(keyms[i].name, key.get()) == 0) - { - strcpy_s(keyms[i].action, action.UTF8String); +bindkey(OFString *key, OFString *action) +{ + for (KeyMapping *mapping in keyMappings) { + if ([mapping.name caseInsensitiveCompare:key] == + OFOrderedSame) { + mapping.action = action; return; } - conoutf(@"unknown key \"%s\"", key.get()); } + + conoutf(@"unknown key \"%@\"", key); } COMMANDN(bind, bindkey, ARG_2STR) void saycommand(char *init) // turns input to the command line on or off @@ -242,35 +257,35 @@ saycommand(NULL); } else if (code == SDLK_ESCAPE) { saycommand(NULL); }; }; - } else if (!menukey(code, isdown)) // keystrokes go to menu - { - loopi(numkm) if (keyms[i].code == - code) // keystrokes go to game, lookup in - // keymap and execute - { - string temp; - strcpy_s(temp, keyms[i].action); - execute(temp, isdown); - return; - }; - }; -}; + } else if (!menukey(code, isdown)) { + // keystrokes go to menu + + for (KeyMapping *mapping in keyMappings) { + if (mapping.code == code) { + // keystrokes go to game, lookup in keymap and + // execute + string temp; + strcpy_s(temp, mapping.action.UTF8String); + execute(temp, isdown); + return; + } + } + } +} char * getcurcommand() { return saycommandon ? commandbuf : NULL; -}; +} void -writebinds(FILE *f) -{ - loopi(numkm) - { - if (*keyms[i].action) - fprintf(f, "bind \"%s\" [%s]\n", keyms[i].name, - keyms[i].action); - }; -}; +writebinds(OFStream *stream) +{ + for (KeyMapping *mapping in keyMappings) + if (mapping.action.length > 0) + [stream writeFormat:@"bind \"%@\" [%@]\n", mapping.name, + mapping.action]; +} Index: src/cube.h ================================================================== --- src/cube.h +++ src/cube.h @@ -9,11 +9,11 @@ #include "tools.h" @interface Cube : OFObject @property (class, readonly, nonatomic) Cube *sharedInstance; @property (readonly, nonatomic) SDL_Window *window; -@property (readonly, nonatomic) OFIRI *gameDataIRI; +@property (readonly, nonatomic) OFIRI *gameDataIRI, *userDataIRI; @property (nonatomic) bool repeatsKeys; @property (nonatomic) int framesInMap; @end enum // block types, order matters! Index: src/protos.h ================================================================== --- src/protos.h +++ src/protos.h @@ -19,11 +19,11 @@ // console extern void keypress(int code, bool isdown, int cooked); extern void renderconsole(); extern void conoutf(OFConstantString *format, ...); extern char *getcurcommand(); -extern void writebinds(FILE *f); +extern void writebinds(OFStream *stream); // init extern void enqueueInit(void (^init)(void)); extern void processInitQueue(void); @@ -81,11 +81,11 @@ extern void neterr(OFString *s); extern void initclientnet(); extern bool netmapstart(); extern int getclientnum(); extern void changemapserv(char *name, int mode); -extern void writeclientinfo(FILE *f); +extern void writeclientinfo(OFStream *stream); // clientgame extern void mousemove(int dx, int dy); extern void updateworld(int millis); extern void startmap(char *name);