37#define WARNINGS OOLITE_DEBUG
39#define OO_EXPANDER_RANDOM (context->useGoodRNG ? (Ranrot()&0xFF) : gen_rnd_number())
97static void AppendCharacters(NSMutableString **result,
const unichar *characters, NSUInteger start, NSUInteger end);
106static NSString *
ExpandKey(
OOStringExpansionContext *context,
const unichar *characters, NSUInteger
size, NSUInteger idx, NSUInteger *replaceLength, NSUInteger sizeLimit, NSUInteger recursionLimit);
127static NSString *
ApplyOperators(NSString *
string, NSString *operatorsString);
128static NSString *
ApplyOneOperator(NSString *
string, NSString *op, NSString *param);
148#define SyntaxError(CONTEXT, CLASS, FORMAT, ...) SyntaxIssue(CONTEXT, OOLOG_FUNCTION_NAME, OOLOG_FILE_NAME, __LINE__, CLASS, OOLOG_WARNING_PREFIX, FORMAT, ## __VA_ARGS__)
151#define SyntaxWarning(CONTEXT, CLASS, FORMAT, ...) SyntaxIssue(CONTEXT, OOLOG_FUNCTION_NAME, OOLOG_FILE_NAME, __LINE__, CLASS, OOLOG_WARNING_PREFIX, FORMAT, ## __VA_ARGS__)
153#define SyntaxWarning(...) do {} while (0)
162 if (
string ==
nil)
return nil;
167 .systemName = [systemName retain],
168 .overrides = [overrides retain],
169 .legacyLocals = [legacyLocals retain],
187 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
188 NSString *result =
nil, *intermediate =
nil;
202 result = intermediate;
211 [context.systemName release];
212 [context.overrides release];
213 [context.legacyLocals release];
214 [context.systemNameWithIan release];
215 [context.randomNameN release];
216 [context.randomNameR release];
217 [context.systemDescriptions release];
225 result = [result copy];
227 return [result autorelease];
240 return [[UNIVERSE systemManager] getRandomSeedForCurrentSystem];
261 const NSUInteger
size = [string length];
264 if (
EXPECT_NOT(
size > sizeLimit || recursionLimit == 0))
return string;
269 if (
size == 0)
return string;
271 unichar characters[size];
272 [string getCharacters:characters range:(NSRange){ 0, size }];
277 NSUInteger copyRangeStart = 0;
280 NSMutableString *result =
nil;
285 for (NSUInteger idx = 0; idx <
size - 1; idx++)
292 NSString *replacement =
nil;
293 NSUInteger replaceLength = 0;
294 unichar thisChar = characters[idx];
298 replacement =
ExpandKey(context, characters,
size, idx, &replaceLength, sizeLimit, recursionLimit);
300 else if (thisChar ==
'%')
304 else if (thisChar ==
']')
306 SyntaxWarning(context,
@"strings.expand.warning.unbalancedClosingBracket",
@"%@",
@"Unbalanced ] in string.");
310 if (characters[idx + 1] ==
'n')
322 if (replacement !=
nil)
328 if ([replacement isEqualToString:
@"\x7F"] && replaceLength <
size)
335 if (copyRangeStart == 0 && replaceLength ==
size)
343 [result appendString:replacement];
346 idx += replaceLength - 1;
347 copyRangeStart = idx + 1;
379static NSString *
ExpandKey(
OOStringExpansionContext *context,
const unichar *characters, NSUInteger
size, NSUInteger idx, NSUInteger *replaceLength, NSUInteger sizeLimit, NSUInteger recursionLimit)
381 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
382 NSCParameterAssert(characters[idx] ==
'[');
385 NSUInteger end, balanceCount = 1, firstBar = 0;
386 bool allDigits =
true;
388 for (end = idx + 1; end < size && balanceCount > 0; end++)
390 if (characters[end] ==
']') balanceCount--;
393 if (characters[end] ==
'[') balanceCount++;
394 else if (characters[end] ==
'|' && firstBar == 0) firstBar = end;
395 if (!isdigit(characters[end]) && firstBar == 0) allDigits =
false;
402 SyntaxWarning(context,
@"strings.expand.warning.unbalancedOpeningBracket",
@"%@",
@"Unbalanced [ in string.");
406 NSUInteger totalLength = end - idx;
407 *replaceLength = totalLength;
408 NSUInteger keyStart = idx + 1, keyLength = totalLength - 2;
409 if (firstBar != 0) keyLength = firstBar - idx - 1;
413 SyntaxWarning(context,
@"strings.expand.warning.emptyKey",
@"%@",
@"Invalid expansion code [] string. (To avoid this message, use %%[%%].)");
417 NSString *expanded =
nil;
420 expanded =
ExpandDigitKey(context, characters, keyStart, keyLength, sizeLimit, recursionLimit);
424 NSString *key = [NSString stringWithCharacters:characters + keyStart length:keyLength];
430 NSString *operators = [NSString stringWithCharacters:characters + firstBar + 1 length:end - firstBar - 2];
446 NSArray *operators = [operatorsString componentsSeparatedByString:@"|"];
449 foreach(op, operators)
451 NSString *param =
nil;
452 NSRange colon = [op rangeOfString:@":"];
453 if (colon.location != NSNotFound)
455 param = [op substringFromIndex:colon.location + colon.length];
456 op = [op substringToIndex:colon.location];
467 return OOCredits([
string doubleValue] * 10);
473 return OOCredits([
string longLongValue]);
485 return OOIntCredits(round([
string doubleValue] / 10.0));
491 return [NSString stringWithFormat:@"%.*f", [param intValue], [string doubleValue]];
497 return [NSString stringWithFormat:@"%g", [string doubleValue] * [param doubleValue]];
503 return [NSString stringWithFormat:@"%g", [string doubleValue] + [param doubleValue]];
518 static NSDictionary *operators =
nil;
520 if (operators ==
nil)
522 #define OPERATOR(name) [NSValue valueWithPointer:Operator_##name], @#name
523 operators = [[NSDictionary alloc] initWithObjectsAndKeys:
534 NSString *(*operator)(NSString *string, NSString *param) = [[operators objectForKey:op] pointerValue];
535 if (
operator != NULL)
537 return operator(
string, param);
540 OOLogERR(
@"strings.expand.invalidOperator",
@"Unknown string expansion operator %@", op);
557 NSCParameterAssert(context != NULL && characters != NULL);
559 NSUInteger keyValue = 0, idx;
560 for (idx = keyStart; idx < (keyStart + keyLength); idx++)
562 NSCAssert2(isdigit(characters[idx]),
@"%s called with non-numeric key [%@].", __FUNCTION__, [NSString stringWithCharacters:characters + keyStart length:keyLength]);
564 keyValue = keyValue * 10 + characters[idx] -
'0';
569 NSArray *entry = [sysDescs oo_arrayAtIndex:keyValue];
575 SyntaxWarning(context,
@"strings.expand.warning.outOfRangeKey",
@"Out-of-range system description expansion key [%@] in string.", [NSString stringWithCharacters:characters + keyStart length:keyLength]);
580 OOLogERR(
@"strings.expand.invalidData",
@"%@",
@"descriptions.plist entry system_description must be an array of arrays of strings.");
586 NSUInteger selection,
count = [entry count];
591 if (rnd >= 0xCC) selection = 4;
592 else if (rnd >= 0x99) selection = 3;
593 else if (rnd >= 0x66) selection = 2;
594 else if (rnd >= 0x33) selection = 1;
600 selection = (rnd *
count) / 256;
604 NSString *
string = [entry oo_stringAtIndex:selection];
605 return Expand(context,
string, sizeLimit, recursionLimit);
616 NSCParameterAssert(context != NULL && key !=
nil);
660 NSCParameterAssert(context != NULL && key !=
nil);
662 id value = [context->overrides objectForKey:key];
666 if (![value isKindOfClass:[NSString
class]] && ![value isKindOfClass:[NSNumber
class]])
668 SyntaxWarning(context,
@"strings.expand.warning.invalidOverride",
@"String expansion override value %@ for [%@] is not a string or number.", [value shortDescription], key);
671 return [value description];
686 NSCParameterAssert(context != NULL && key !=
nil);
689 SEL selector = NSMapGet(specials, key);
690 if (selector != NULL)
692 NSCAssert2([
PLAYER respondsToSelector:selector],
@"Special string expansion selector %@ for [%@] is not implemented.", NSStringFromSelector(selector), key);
694 NSString *result = [PLAYER performSelector:selector];
697 NSCAssert2([result isKindOfClass:[NSString
class]],
@"Special string expansion [%@] expanded to %@, but expected a string.", key, [result shortDescription]);
712 NSCParameterAssert(context != NULL && key !=
nil);
713 if ([key hasPrefix:
@"oolite_key_"])
715 NSString *binding = [key substringFromIndex:7];
716 return [PLAYER keyBindingDescription2:binding];
729 static NSMapTable *specials = NULL;
730 if (specials != NULL)
return specials;
732 struct { NSString *key;
SEL selector; } selectors[] =
734 {
@"commander_name",
@selector(commanderName_string) },
735 {
@"commander_shipname",
@selector(commanderShip_string) },
736 {
@"commander_shipdisplayname",
@selector(commanderShipDisplayName_string) },
737 {
@"commander_rank",
@selector(commanderRank_string) },
738 {
@"commander_kills",
@selector(commanderKillsAsString) },
739 {
@"commander_legal_status",
@selector(commanderLegalStatus_string) },
740 {
@"commander_bounty",
@selector(commanderBountyAsString) },
741 {
@"credits_number",
@selector(creditsFormattedForSubstitution) },
742 {
@"_oo_legacy_credits_number",
@selector(creditsFormattedForLegacySubstitution) }
744 unsigned i,
count =
sizeof selectors /
sizeof *selectors;
746 specials = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks,
count);
747 for (i = 0; i <
count; i++)
749 NSMapInsertKnownAbsent(specials, selectors[i].key, selectors[i].selector);
766 id value = [[UNIVERSE descriptions] objectForKey:key];
769 if ([value isKindOfClass:[NSArray
class]] && [value
count] > 0)
772 value = [value oo_objectAtIndex:rnd];
775 if (![value isKindOfClass:[NSString
class]])
778 OOLogERR(
@"strings.expand.invalidData",
@"String expansion value %@ for [%@] from descriptions.plist is not a string or number.", [value shortDescription], key);
783 return Expand(context, value, sizeLimit, recursionLimit);
796 if ([key hasPrefix:
@"mission_"])
798 return [PLAYER missionVariableForKey:key];
813 return [[context->legacyLocals objectForKey:key] description];
825 NSCParameterAssert(context != NULL && key !=
nil);
829 if (selector != NULL)
831 return [[PLAYER performSelector:selector] description];
848 static NSMapTable *selectorCache = NULL;
851 if (selectorCache != NULL)
853 selector = NSMapGet(selectorCache, key);
856 if (selector == NULL)
858 static NSDictionary *aliases =
nil;
859 static NSSet *whitelist =
nil;
860 if (whitelist ==
nil)
863 whitelist = [[NSSet alloc] initWithArray:[whitelistDict oo_arrayForKey:@"query_methods"]];
864 aliases = [[whitelistDict oo_dictionaryForKey:@"query_method_aliases"] copy];
867 NSString *selectorName = [aliases oo_stringForKey:key];
868 if (selectorName ==
nil) selectorName = key;
870 if ([whitelist containsObject:selectorName])
872 selector = NSSelectorFromString(selectorName);
878 NSCAssert1([
PLAYER respondsToSelector:selector],
@"Player does not respond to whitelisted query selector %@.", key);
881 if (selector != NULL)
884 if (selectorCache == NULL)
886 selectorCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, [whitelist
count]);
888 NSMapInsertKnownAbsent(selectorCache, key, selector);
905 if ([key hasSuffix:
@"_string"] || [key hasSuffix:
@"_number"] || [key hasSuffix:
@"_bool"])
907 SyntaxError(context,
@"strings.expand.invalidSelector",
@"Unpermitted legacy script method [%@] in string.", key);
911 SyntaxWarning(context,
@"strings.expand.warning.unknownExpansion",
@"Unknown expansion key [%@] in string.", key);
939 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
940 NSCParameterAssert(characters[idx] ==
'%');
944 unichar selector = characters[idx + 1];
997 SyntaxWarning(context,
@"strings.expand.warning.unknownPercentEscape",
@"Unknown escape code in string: %%%lc. (To encode a %% sign without this warning, use %%%% - but prefer \"percent\
" in prose writing.)", selector);
1011 NSRange containsR = [input rangeOfString:@"%R"];
1012 if (containsR.location == NSNotFound)
1017 NSMutableString *output = [NSMutableString stringWithString:input];
1032 [output replaceOccurrencesOfString:@"%R" withString:percentR options:NSLiteralSearch range:NSMakeRange(0, [output length])];
1033 }
while([output rangeOfString:
@"%R"].location != NSNotFound);
1035 return [NSString stringWithString:output];
1046 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
1047 NSCParameterAssert(characters[idx + 1] ==
'G');
1052 #define kInvalidGEscapeMessage @"String escape code %G must be followed by six integers."
1060 char hundreds = characters[idx + 2];
1061 char tens = characters[idx + 3];
1062 char units = characters[idx + 4];
1063 char galHundreds = characters[idx + 5];
1064 char galTens = characters[idx + 6];
1065 char galUnits = characters[idx + 7];
1067 if (!(isdigit(hundreds) && isdigit(tens) && isdigit(units) && isdigit(galHundreds) && isdigit(galTens) && isdigit(galUnits)))
1073 OOSystemID sysID = (hundreds -
'0') * 100 + (tens -
'0') * 10 + (units -
'0');
1076 SyntaxError(context,
@"strings.expand.invalidJEscape.range",
@"String escape code %%G%3u for system is out of range (must be less than %u).", sysID,
kOOMaximumSystemID + 1);
1080 OOGalaxyID galID = (galHundreds -
'0') * 100 + (galTens -
'0') * 10 + (galUnits -
'0');
1083 SyntaxError(context,
@"strings.expand.invalidJEscape.range",
@"String escape code %%G%3u for galaxy is out of range (must be less than %u).", galID,
kOOMaximumGalaxyID + 1);
1087 return [UNIVERSE getSystemName:sysID forGalaxy:galID];
1098 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
1099 NSCParameterAssert(characters[idx + 1] ==
'J');
1104 #define kInvalidJEscapeMessage @"String escape code %J must be followed by three integers."
1112 char hundreds = characters[idx + 2];
1113 char tens = characters[idx + 3];
1114 char units = characters[idx + 4];
1116 if (!(isdigit(hundreds) && isdigit(tens) && isdigit(units)))
1122 OOSystemID sysID = (hundreds -
'0') * 100 + (tens -
'0') * 10 + (units -
'0');
1125 SyntaxError(context,
@"strings.expand.invalidJEscape.range",
@"String escape code %%J%3u is out of range (must be less than %u).", sysID,
kOOMaximumSystemID + 1);
1129 return [UNIVERSE getSystemName:sysID];
1133static void AppendCharacters(NSMutableString **result,
const unichar *characters, NSUInteger start, NSUInteger end)
1135 NSCParameterAssert(result != NULL && characters != NULL && start <= end);
1140 *result = [NSMutableString string];
1143 if (start == end)
return;
1153 CFStringAppendCharacters((CFMutableStringRef)*result, characters + start, end - start);
1155 NSString *temp = [[NSString alloc] initWithCharacters:characters + start length:end - start];
1156 [*result appendString:temp];
1164 NSCParameterAssert(context != NULL);
1166 context->
systemName = [[UNIVERSE getSystemName:[PLAYER systemID]] retain];
1175 NSCParameterAssert(context != NULL);
1179 context->
systemNameWithIan = [OOExpandWithOptions(context->seed, kOOExpandDisallowPercentI | kOOExpandGoodRNG | kOOExpandKey, @"planetname-possessive") retain];
1188 NSCParameterAssert(context != NULL);
1192 context->
randomNameN = [NewRandomDigrams(context) retain];
1201 NSCParameterAssert(context != NULL);
1205 context->
randomNameR = [OldRandomDigrams() retain];
1214 NSCParameterAssert(context != NULL);
1218 context->
systemDescriptions = [[[UNIVERSE descriptions] oo_arrayForKey:@"system_description"] retain];
1219 context->
sysDescCount = [context->systemDescriptions count];
1235 NSString *digrams = [[UNIVERSE descriptions] objectForKey:@"digrams"];
1236 NSMutableString *name = [NSMutableString stringWithCapacity:256];
1238 for (
unsigned i = 0; i <=len; i++)
1241 [name appendString:[digrams substringWithRange:NSMakeRange(x, 2)]];
1244 return [name capitalizedString];
1254 NSString *digrams = [[UNIVERSE descriptions] objectForKey:@"digrams"];
1255 NSUInteger
count = [digrams length] / 2;
1256 NSMutableString *name = [NSMutableString stringWithCapacity:length * 2];
1258 for (
unsigned i = 0; i != length; ++i)
1260 [name appendString:[digrams substringWithRange:NSMakeRange((OO_EXPANDER_RANDOM % count) * 2, 2)]];
1263 return [name capitalizedString];
1269 NSCParameterAssert(context != NULL);
1272 va_start(args, format);
1288 format = [prefix stringByAppendingString:format];
#define OO_TAKES_FORMAT_STRING(stringIndex, firstToCheck)
void OOJSReportWarningWithArguments(JSContext *context, NSString *format, va_list args)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE void OOJSRelinquishContext(JSContext *context)
#define OOLogERR(class, format,...)
void void void OOLogWithFunctionFileAndLineAndArguments(NSString *inMessageClass, const char *inFunction, const char *inFile, unsigned long inLine, NSString *inFormat, va_list inArguments) OO_TAKES_FORMAT_STRING(5
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
NSUInteger OOExpandOptions
@ kOOExpandForJavaScript
Report warnings through JavaScript runtime system instead of normal logging.
@ kOOExpandDisallowPercentI
Disallow I expansion (used when expanding I itself).
@ kOOExpandKey
Treat string as a key. Expand("foo", kOOExpandKey) == Expand(@"[foo]", kOOExpandNoOptions).
@ kOOExpandBackslashN
Convert literal "\\n"s to line breaks (used for missiontext.plist for historical reasons).
@ kOOExpandGoodRNG
Use RANDROT for selecting from description arrays and for N expansion.
@ kOOExpandReseedRNG
Set "really random" seeds while expanding.
static void SyntaxIssue(OOStringExpansionContext *context, const char *function, const char *fileName, NSUInteger line, NSString *logMessageClass, NSString *prefix, NSString *format,...) OO_TAKES_FORMAT_STRING(7
NSString * OOGenerateSystemDescription(Random_Seed seed, NSString *name)
static NSString * ExpandStringKeyLegacyLocalVariable(OOStringExpansionContext *context, NSString *key)
static NSString * GetSystemName(OOStringExpansionContext *context)
#define kInvalidGEscapeMessage
static NSString * Operator_icr(NSString *string, NSString *param)
#define SyntaxError(CONTEXT, CLASS, FORMAT,...)
static NSString * ExpandStringKeyFromDescriptions(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit)
static NSArray * GetSystemDescriptions(OOStringExpansionContext *context)
#define OO_EXPANDER_RANDOM
static NSString * Expand(OOStringExpansionContext *context, NSString *string, NSUInteger sizeLimit, NSUInteger recursionLimit)
static NSString * ExpandDigitKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger keyStart, NSUInteger keyLength, NSUInteger sizeLimit, NSUInteger recursionLimit)
Random_Seed OOStringExpanderDefaultRandomSeed(void)
static NSString * ExpandLegacyScriptSelectorKey(OOStringExpansionContext *context, NSString *key)
static NSString * ApplyOperators(NSString *string, NSString *operatorsString)
static NSString * ExpandSystemNameEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
static NSString * ExpandStringKeyMissionVariable(OOStringExpansionContext *context, NSString *key)
#define SyntaxWarning(CONTEXT, CLASS, FORMAT,...)
static NSString * Operator_dcr(NSString *string, NSString *param)
static NSString * GetSystemNameIan(OOStringExpansionContext *context)
static NSString * ExpandStringKeyOverride(OOStringExpansionContext *context, NSString *key)
static NSString * OldRandomDigrams(void)
static NSString * Operator_cr(NSString *string, NSString *param)
#define kInvalidJEscapeMessage
static NSString * Operator_precision(NSString *string, NSString *param)
static NSString * ExpandStringKey(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit)
static SEL LookUpLegacySelector(NSString *key)
static NSString * ExpandPercentEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
static NSString * GetRandomNameR(OOStringExpansionContext *context)
static NSString * Operator_multiply(NSString *string, NSString *param)
static NSString * ExpandSystemNameForGalaxyEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
static NSString * NewRandomDigrams(OOStringExpansionContext *context)
NSString * OOExpandDescriptionString(Random_Seed seed, NSString *string, NSDictionary *overrides, NSDictionary *legacyLocals, NSString *systemName, OOExpandOptions options)
static void ReportWarningForUnknownKey(OOStringExpansionContext *context, NSString *key)
static NSString * ApplyOneOperator(NSString *string, NSString *op, NSString *param)
static NSString * ExpandStringKeyKeyboardBinding(OOStringExpansionContext *context, NSString *key)
static void AppendCharacters(NSMutableString **result, const unichar *characters, NSUInteger start, NSUInteger end)
static NSString * ExpandStringKeySpecial(OOStringExpansionContext *context, NSString *key)
static NSMapTable * SpecialSubstitutionSelectors(void)
static NSString * Operator_idcr(NSString *string, NSString *param)
static NSString * ExpandKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength, NSUInteger sizeLimit, NSUInteger recursionLimit)
static NSString * GetRandomNameN(OOStringExpansionContext *context)
static NSString * ExpandPercentR(OOStringExpansionContext *context, NSString *input)
static NSString * Operator_add(NSString *string, NSString *param)
OOINLINE NSString * OOCredits(OOCreditsQuantity tenthsOfCredits)
OOINLINE NSString * OOIntCredits(OOCreditsQuantity integerCredits)
NSDictionary * whitelistDictionary()
void seed_RNG_only_for_planet_description(Random_Seed s_seed)
void OORestoreRandomState(OORandomState state)
OORandomState OOSaveRandomState(void)
void OOSetReallyRandomRANROTAndRndSeeds(void)
NSString * systemNameWithIan
NSDictionary * legacyLocals
NSArray * systemDescriptions