From 38747afbb58deae98d5bb819d8ae5ca8c6fa122f Mon Sep 17 00:00:00 2001 From: Jonathan Schleifer Date: Wed, 12 Mar 2025 00:16:05 +0000 Subject: [PATCH] Be more tolerant of invalid arguments FossilOrigin-Name: d2f07d884a4f4319ff687e085c3233e210ee05b46a2675ecd757a261e6f19e6e --- src/Command.mm | 21 ++++---- src/MenuItem.m | 7 ++- src/clientgame.mm | 3 +- src/commands.mm | 122 ++++++++++++++++++++++---------------------- src/console.mm | 5 +- src/editing.mm | 5 +- src/meson.build | 1 + src/renderextras.mm | 3 ++ src/rendergl.mm | 9 +++- src/rendermd2.mm | 12 +++-- src/sound.mm | 5 +- src/weapon.mm | 7 +-- 12 files changed, 112 insertions(+), 88 deletions(-) diff --git a/src/Command.mm b/src/Command.mm index ef70523..d0d2d7c 100644 --- a/src/Command.mm +++ b/src/Command.mm @@ -1,4 +1,5 @@ #import "Command.h" +#import "OFString+Cube.h" #include @@ -38,34 +39,34 @@ padArguments(OFArray *arguments, size_t count) if (isDown) { arguments = padArguments(arguments, 2); ((void(__cdecl *)(int))_function)( - [arguments[1] intValueWithBase:0]); + [arguments[1] cube_intValueWithBase:0]); } break; case ARG_2INT: if (isDown) { arguments = padArguments(arguments, 3); ((void(__cdecl *)(int, int))_function)( - [arguments[1] intValueWithBase:0], - [arguments[2] intValueWithBase:0]); + [arguments[1] cube_intValueWithBase:0], + [arguments[2] cube_intValueWithBase:0]); } break; case ARG_3INT: if (isDown) { arguments = padArguments(arguments, 4); ((void(__cdecl *)(int, int, int))_function)( - [arguments[1] intValueWithBase:0], - [arguments[2] intValueWithBase:0], - [arguments[3] intValueWithBase:0]); + [arguments[1] cube_intValueWithBase:0], + [arguments[2] cube_intValueWithBase:0], + [arguments[3] cube_intValueWithBase:0]); } break; case ARG_4INT: if (isDown) { arguments = padArguments(arguments, 5); ((void(__cdecl *)(int, int, int, int))_function)( - [arguments[1] intValueWithBase:0], - [arguments[2] intValueWithBase:0], - [arguments[3] intValueWithBase:0], - [arguments[4] intValueWithBase:0]); + [arguments[1] cube_intValueWithBase:0], + [arguments[2] cube_intValueWithBase:0], + [arguments[3] cube_intValueWithBase:0], + [arguments[4] cube_intValueWithBase:0]); } break; case ARG_NONE: diff --git a/src/MenuItem.m b/src/MenuItem.m index ec531c4..bba4e96 100644 --- a/src/MenuItem.m +++ b/src/MenuItem.m @@ -23,12 +23,17 @@ x = _text.intValue; } @catch (OFInvalidFormatException *e) { x = 0; + } @catch (OFOutOfRangeException *e) { + x = 0; } - @try { + @ + try { y = otherItem.text.intValue; } @catch (OFInvalidFormatException *e) { y = 0; + } @catch (OFOutOfRangeException *e) { + y = 0; } if (x > y) diff --git a/src/clientgame.mm b/src/clientgame.mm index 208b44b..b261021 100644 --- a/src/clientgame.mm +++ b/src/clientgame.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "OFString+Cube.h" int nextmode = 0; // nextmode becomes gamemode after next map load VAR(gamemode, 1, 0, 0); @@ -238,7 +239,7 @@ static OFString *sleepcmd = nil; void sleepf(OFString *msec, OFString *cmd) { - sleepwait = msec.intValue + lastmillis; + sleepwait = msec.cube_intValue + lastmillis; sleepcmd = cmd; } COMMANDN(sleep, sleepf, ARG_2STR) diff --git a/src/commands.mm b/src/commands.mm index c9eb6cd..bbeaf43 100644 --- a/src/commands.mm +++ b/src/commands.mm @@ -8,6 +8,7 @@ #import "Alias.h" #import "Command.h" #import "Identifier.h" +#import "OFString+Cube.h" #import "Variable.h" // contains ALL vars/commands/aliases @@ -181,6 +182,54 @@ lookup(OFString *n) // find value of ident referenced with $ in exp return n; } +int +executeIdentifier(__kindof Identifier *identifier, + OFArray *arguments, bool isDown) +{ + if (identifier == nil) { + @try { + return [arguments[0] intValueWithBase:0]; + } @catch (OFInvalidFormatException *e) { + conoutf(@"unknown command: %@", arguments[0]); + return 0; + } @catch (OFOutOfRangeException *e) { + conoutf(@"invalid value: %@", arguments[0]); + return 0; + } + } + + if ([identifier isKindOfClass:Command.class]) + // game defined commands use very ad-hoc function signature, + // and just call it + return [identifier callWithArguments:arguments isDown:isDown]; + + if ([identifier isKindOfClass:Variable.class]) { + if (!isDown) + return 0; + + // game defined variables + if (arguments.count < 2 || arguments[1].length == 0) + [identifier printValue]; + else + [identifier + setValue:[arguments[1] cube_intValueWithBase:0]]; + } + + if ([identifier isKindOfClass:Alias.class]) { + // alias, also used as functions and (global) variables + for (int i = 1; i < arguments.count; i++) { + // set any arguments as (global) arg values so + // functions can access them + OFString *t = [OFString stringWithFormat:@"arg%d", i]; + alias(t, arguments[i]); + } + + return execute([identifier action], isDown); + } + + return 0; +} + // all evaluation happens here, recursively int execute(OFString *string, bool isDown) @@ -230,55 +279,8 @@ execute(OFString *string, bool isDown) if (c.length == 0) continue; - __kindof Identifier *identifier = identifiers[c]; - if (identifier == nil) { - @try { - val = [c intValueWithBase:0]; - } @catch (OFInvalidFormatException *e) { - conoutf(@"unknown command: %@", c); - } - } else { - if ([identifier isKindOfClass:Command.class]) { - // game defined commands use very - // ad-hoc function signature, and just - // call it - OFArray *arguments = - [[OFArray alloc] - initWithObjects:w - count:numargs]; - val = [identifier - callWithArguments:arguments - isDown:isDown]; - } else if ([identifier - isKindOfClass:Variable.class]) { - // game defined variables - if (isDown) { - if (w[1].length == 0) - [identifier printValue]; - else - [identifier - setValue: - [w[1] - intValueWithBase: - 0]]; - } - } else if ([identifier - isKindOfClass:Alias.class]) { - // alias, also used as functions and - // (global) variables - for (int i = 1; i < numargs; i++) { - // set any arguments as - // (global) arg values so - // functions can access them - OFString *t = [OFString - stringWithFormat:@"arg%d", - i]; - alias(t, w[i]); - } - val = execute( - [identifier action], isDown); - } - } + val = executeIdentifier(identifiers[c], + [OFArray arrayWithObjects:w count:numargs], isDown); } return val; @@ -378,13 +380,12 @@ writecfg() return; } - [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"]; + [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"]; @@ -418,8 +419,8 @@ writecfg() COMMAND(writecfg, ARG_NONE) // below the commands that implement a small imperative language. thanks to the -// semantics of -// () and [] expressions, any control construct can be defined trivially. +// semantics of () and [] expressions, any control construct can be defined +// trivially. void intset(OFString *name, int v) @@ -439,7 +440,7 @@ void loopa(OFString *times, OFString *body) { @autoreleasepool { - int t = times.intValue; + int t = times.cube_intValue; loopi(t) { @@ -497,10 +498,9 @@ void at(OFString *s_, OFString *pos) { @autoreleasepool { - int n = pos.intValue; + int n = pos.cube_intValue; std::unique_ptr copy(strdup(s_.UTF8String)); char *s = copy.get(); - loopi(n) s += strspn(s += strcspn(s, " \0"), " "); s[strcspn(s, " \0")] = 0; concat(@(s)); diff --git a/src/console.mm b/src/console.mm index 77ed3d7..58ec78b 100644 --- a/src/console.mm +++ b/src/console.mm @@ -5,6 +5,7 @@ #include #import "KeyMapping.h" +#import "OFString+Cube.h" struct cline { char *cref; @@ -106,8 +107,8 @@ keymap(OFString *code, OFString *key, OFString *action) if (keyMappings == nil) keyMappings = [[OFMutableArray alloc] init]; - KeyMapping *mapping = [[KeyMapping alloc] initWithCode:code.intValue - name:key]; + KeyMapping *mapping = + [[KeyMapping alloc] initWithCode:code.cube_intValue name:key]; mapping.action = action; [keyMappings addObject:mapping]; } diff --git a/src/editing.mm b/src/editing.mm index bc098ff..52c7013 100644 --- a/src/editing.mm +++ b/src/editing.mm @@ -4,6 +4,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "OFString+Cube.h" bool editmode = false; @@ -605,8 +606,8 @@ newent(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4) EDITSEL; @autoreleasepool { newentity(sel.x, sel.y, (int)player1.o.z, what, - [a1 intValueWithBase:0], [a2 intValueWithBase:0], - [a3 intValueWithBase:0], [a4 intValueWithBase:0]); + [a1 cube_intValueWithBase:0], [a2 cube_intValueWithBase:0], + [a3 cube_intValueWithBase:0], [a4 cube_intValueWithBase:0]); } } diff --git a/src/meson.build b/src/meson.build index 0363976..d969438 100644 --- a/src/meson.build +++ b/src/meson.build @@ -10,6 +10,7 @@ executable('client', 'MapModelInfo.m', 'Menu.m', 'MenuItem.m', + 'OFString+Cube.mm', 'Projectile.m', 'ServerInfo.mm', 'Variable.mm', diff --git a/src/renderextras.mm b/src/renderextras.mm index 41a7ae0..8f7e978 100644 --- a/src/renderextras.mm +++ b/src/renderextras.mm @@ -206,6 +206,9 @@ loadsky(OFString *basename) @autoreleasepool { static OFString *lastsky = @""; + basename = [basename stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]; + if ([lastsky isEqual:basename]) return; diff --git a/src/rendergl.mm b/src/rendergl.mm index 672caf5..c83224c 100644 --- a/src/rendergl.mm +++ b/src/rendergl.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "OFString+Cube.h" #ifdef DARWIN # define GL_COMBINE_EXT GL_COMBINE_ARB @@ -197,11 +198,15 @@ void texture(OFString *aframe, OFString *name) { @autoreleasepool { - int num = curtexnum++, frame = aframe.intValue; + int num = curtexnum++, frame = aframe.cube_intValue; + if (num < 0 || num >= 256 || frame < 0 || frame >= MAXFRAMES) return; + mapping[num][frame] = 1; - mapname[num][frame] = name; + mapname[num][frame] = + [name stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]; } } COMMAND(texture, ARG_2STR) diff --git a/src/rendermd2.mm b/src/rendermd2.mm index 6902248..d2096d1 100644 --- a/src/rendermd2.mm +++ b/src/rendermd2.mm @@ -5,6 +5,7 @@ #import "DynamicEntity.h" #import "MD2.h" #import "MapModelInfo.h" +#import "OFString+Cube.h" static OFMutableDictionary *mdllookup = nil; static OFMutableArray *mapmodels = nil; @@ -67,11 +68,12 @@ void mapmodel( OFString *rad, OFString *h, OFString *zoff, OFString *snap, OFString *name) { - MD2 *m = loadmodel(name); - m.mmi = [[MapModelInfo alloc] initWithRad:rad.intValue - h:h.intValue - zoff:zoff.intValue - snap:snap.intValue + MD2 *m = loadmodel([name stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]); + m.mmi = [[MapModelInfo alloc] initWithRad:rad.cube_intValue + h:h.cube_intValue + zoff:zoff.cube_intValue + snap:snap.cube_intValue name:m.loadname]; if (mapmodels == nil) diff --git a/src/sound.mm b/src/sound.mm index f0f090d..76b3d48 100644 --- a/src/sound.mm +++ b/src/sound.mm @@ -88,6 +88,8 @@ music(OFString *name) stopsound(); if (soundvol && musicvol) { @autoreleasepool { + name = [name stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]; OFString *path = [OFString stringWithFormat:@"packages/%@", name]; OFIRI *IRI = [Cube.sharedInstance.gameDataIRI @@ -148,7 +150,8 @@ registersound(OFString *name) if (snames == nil) snames = [[OFMutableArray alloc] init]; - [snames addObject:name]; + [snames addObject:[name stringByReplacingOccurrencesOfString:@"\\" + withString:@"/"]]; samples.add(NULL); return samples.length() - 1; diff --git a/src/weapon.mm b/src/weapon.mm index 0355e04..7774a96 100644 --- a/src/weapon.mm +++ b/src/weapon.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "OFString+Cube.h" #import "Projectile.h" static const int MONSTERDAMAGEFACTOR = 4; @@ -63,9 +64,9 @@ reloadtime(int gun) void weapon(OFString *a1, OFString *a2, OFString *a3) { - selectgun((a1.length > 0 ? a1.intValue : -1), - (a2.length > 0 ? a2.intValue : -1), - (a3.length > 0 ? a3.intValue : -1)); + selectgun((a1.length > 0 ? a1.cube_intValue : -1), + (a2.length > 0 ? a2.cube_intValue : -1), + (a3.length > 0 ? a3.cube_intValue : -1)); } COMMAND(weapon, ARG_3STR)