Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOStringExpander.m
Go to the documentation of this file.
1/*
2
3OOStringExpander.m
4
5
6Oolite
7Copyright (C) 2004-2013 Giles C Williams and contributors
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the impllied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22MA 02110-1301, USA.
23
24*/
25
26#import "OOCocoa.h"
27#import "OOStringExpander.h"
28#import "Universe.h"
31#import "OOStringParsing.h"
32#import "ResourceManager.h"
34#import "PlayerEntity.h"
35
36// Don't bother with syntax warnings in Deployment builds.
37#define WARNINGS OOLITE_DEBUG
38
39#define OO_EXPANDER_RANDOM (context->useGoodRNG ? (Ranrot()&0xFF) : gen_rnd_number())
40
41enum
42{
43 /*
44 Total stack limit for strings being parsed (in UTF-16 code elements,
45 i.e. units of 2 bytes), used recursively - for instance, if the root
46 string takes 10,000 characters, any string it recurses into gets
47 kStackAllocationLimit - 10,000. If the limit would be exceeded,
48 the unexpanded string is returned instead.
49
50 The limit is expected to be much higher than necessary for any practical
51 string, and exists only to catch pathological behaviour without crashing.
52 */
54
55 /*
56 Recursion limit, for much the same purpose. Without it, we crash about
57 22,000 stack frames deep when trying to expand a = "[a]" on a Mac.
58 */
59 kRecursionLimit = 100
60};
61
62
63/* OOStringExpansionContext
64
65 Struct used to store context and caches for the entire string expansion
66 operation, including recursive calls (so it can't contain anything pertaining
67 to the specific string being expanded).
68*/
69typedef struct
70{
72 NSString *systemName;
73 NSDictionary *overrides;
74 NSDictionary *legacyLocals;
77 bool hasPercentR; // Set to indicate we need an ExpandPercentR() pass.
80
81 NSString *systemNameWithIan; // Cache for %I
82 NSString *randomNameN; // Cache for %N
83 NSString *randomNameR; // Cache for %R
84 NSArray *systemDescriptions;// Cache for system_description, used for numbered keys.
85 NSUInteger sysDescCount; // Count of systemDescriptions, valid after GetSystemDescriptions() called.
87
88
89/* Accessors for lazily-instantiated caches in context.
90*/
91static NSString *GetSystemName(OOStringExpansionContext *context); // %H
92static NSString *GetSystemNameIan(OOStringExpansionContext *context); // %I
93static NSString *GetRandomNameN(OOStringExpansionContext *context); // %N
94static NSString *GetRandomNameR(OOStringExpansionContext *context); // %R
95static NSArray *GetSystemDescriptions(OOStringExpansionContext *context);
96
97static void AppendCharacters(NSMutableString **result, const unichar *characters, NSUInteger start, NSUInteger end);
98
99static NSString *NewRandomDigrams(OOStringExpansionContext *context);
100static NSString *OldRandomDigrams(void);
101
102
103// Various bits of expansion logic, each with a comment of its very own at the implementation.
104static NSString *Expand(OOStringExpansionContext *context, NSString *string, NSUInteger sizeLimit, NSUInteger recursionLimit);
105
106static NSString *ExpandKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength, NSUInteger sizeLimit, NSUInteger recursionLimit);
107static NSString *ExpandDigitKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger keyStart, NSUInteger keyLength, NSUInteger sizeLimit, NSUInteger recursionLimit);
108static NSString *ExpandStringKey(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit);
109static NSString *ExpandStringKeyOverride(OOStringExpansionContext *context, NSString *key);
110static NSString *ExpandStringKeySpecial(OOStringExpansionContext *context, NSString *key);
111static NSString *ExpandStringKeyKeyboardBinding(OOStringExpansionContext *context, NSString *key);
112static NSMapTable *SpecialSubstitutionSelectors(void);
113static NSString *ExpandStringKeyFromDescriptions(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit);
114static NSString *ExpandStringKeyMissionVariable(OOStringExpansionContext *context, NSString *key);
115static NSString *ExpandStringKeyLegacyLocalVariable(OOStringExpansionContext *context, NSString *key);
116static NSString *ExpandLegacyScriptSelectorKey(OOStringExpansionContext *context, NSString *key);
117static SEL LookUpLegacySelector(NSString *key);
118
119static NSString *ExpandPercentEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength);
120static NSString *ExpandSystemNameForGalaxyEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength);
121static NSString *ExpandSystemNameEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength);
122static NSString *ExpandPercentR(OOStringExpansionContext *context, NSString *input);
123#if WARNINGS
124static void ReportWarningForUnknownKey(OOStringExpansionContext *context, NSString *key);
125#endif
126
127static NSString *ApplyOperators(NSString *string, NSString *operatorsString);
128static NSString *ApplyOneOperator(NSString *string, NSString *op, NSString *param);
129
130
131/* SyntaxWarning(context, logMessageClass, format, ...)
132 SyntaxError(context, logMessageClass, format, ...)
133
134 Report warning or error for expansion syntax, including unknown keys.
135
136 Warnings are reported as JS warnings or log messages (depending on the
137 context->isJavaScript flag) if the relevant log message class is enabled.
138 Warnings are completely disabled in Deployment builds.
139
140 Errors are reported as JS warnings (not exceptions) or log messages (again
141 depending on context->isJavaScript) in all configurations. Exceptions are
142 not used to avoid breaking code that worked with the old expander, even if
143 it was questionable.
144
145 Errors that are not syntax or invalid keys are reported with OOLogERR().
146*/
147static void SyntaxIssue(OOStringExpansionContext *context, const char *function, const char *fileName, NSUInteger line, NSString *logMessageClass, NSString *prefix, NSString *format, ...) OO_TAKES_FORMAT_STRING(7, 8);
148#define SyntaxError(CONTEXT, CLASS, FORMAT, ...) SyntaxIssue(CONTEXT, OOLOG_FUNCTION_NAME, OOLOG_FILE_NAME, __LINE__, CLASS, OOLOG_WARNING_PREFIX, FORMAT, ## __VA_ARGS__)
149
150#if WARNINGS
151#define SyntaxWarning(CONTEXT, CLASS, FORMAT, ...) SyntaxIssue(CONTEXT, OOLOG_FUNCTION_NAME, OOLOG_FILE_NAME, __LINE__, CLASS, OOLOG_WARNING_PREFIX, FORMAT, ## __VA_ARGS__)
152#else
153#define SyntaxWarning(...) do {} while (0)
154#endif
155
156
157// MARK: -
158// MARK: Public functions
159
160NSString *OOExpandDescriptionString(Random_Seed seed, NSString *string, NSDictionary *overrides, NSDictionary *legacyLocals, NSString *systemName, OOExpandOptions options)
161{
162 if (string == nil) return nil;
163
165 {
166 .seed = seed,
167 .systemName = [systemName retain],
168 .overrides = [overrides retain],
169 .legacyLocals = [legacyLocals retain],
170 .isJavaScript = options & kOOExpandForJavaScript,
171 .convertBackslashN = options & kOOExpandBackslashN,
172 .useGoodRNG = options & kOOExpandGoodRNG
173 };
174
175 // Avoid recursive %I expansion by pre-seeding cache with literal %I.
176 if (options & kOOExpandDisallowPercentI) {
177 context.systemNameWithIan = @"%I";
178 }
179
180 OORandomState savedRandomState;
181 if (options & kOOExpandReseedRNG)
182 {
183 savedRandomState = OOSaveRandomState();
185 }
186
187 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
188 NSString *result = nil, *intermediate = nil;
189 @try
190 {
191 // TODO: profile caching the results. Would need to keep track of whether we've done something nondeterministic (array selection, %R etc).
192 if (options & kOOExpandKey)
193 {
194 intermediate = ExpandStringKey(&context, string, kStackAllocationLimit, kRecursionLimit);
195 }
196 else
197 {
198 intermediate = Expand(&context, string, kStackAllocationLimit, kRecursionLimit);
199 }
200 if (!context.hasPercentR)
201 {
202 result = intermediate;
203 }
204 else
205 {
206 result = ExpandPercentR(&context, intermediate);
207 }
208 }
209 @finally
210 {
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];
218 }
219
220 if (options & kOOExpandReseedRNG)
221 {
222 OORestoreRandomState(savedRandomState);
223 }
224
225 result = [result copy];
226 [pool release];
227 return [result autorelease];
228}
229
230
231NSString *OOGenerateSystemDescription(Random_Seed seed, NSString *name)
232{
234 return OOExpandDescriptionString(seed, @"system-description-string", nil, nil, name, kOOExpandKey);
235}
236
237
239{
240 return [[UNIVERSE systemManager] getRandomSeedForCurrentSystem];
241}
242
243
244// MARK: -
245// MARK: Guts
246
247
248/* Expand(context, string, sizeLimit, recursionLimit)
249
250 Top-level expander. Expands all types of substitution in a string.
251
252 <sizeLimit> is the remaining budget for stack allocation of read buffers.
253 (Expand() is the only function that creates such buffers.) <recursionLimit>
254 limits the number of recursive calls of Expand() that are permitted. If one
255 of the limits would be exceeded, Expand() returns the input string unmodified.
256*/
257static NSString *Expand(OOStringExpansionContext *context, NSString *string, NSUInteger sizeLimit, NSUInteger recursionLimit)
258{
259 NSCParameterAssert(string != nil && context != NULL && sizeLimit <= kStackAllocationLimit);
260
261 const NSUInteger size = [string length];
262
263 // Avoid stack overflow.
264 if (EXPECT_NOT(size > sizeLimit || recursionLimit == 0)) return string;
265 sizeLimit -= size;
266 recursionLimit--;
267
268 // Nothing to expand in an empty string, and the size-1 thing below would be trouble.
269 if (size == 0) return string;
270
271 unichar characters[size];
272 [string getCharacters:characters range:(NSRange){ 0, size }];
273
274 /* Beginning of current range of non-special characters. If we encounter
275 a substitution, we'll be copying from here forward.
276 */
277 NSUInteger copyRangeStart = 0;
278
279 // Mutable string for result if we perform any substitutions.
280 NSMutableString *result = nil;
281
282 /* The iteration limit is size - 1 because every valid substitution is at
283 least 2 characters long. This way, characters[idx + 1] is always valid.
284 */
285 for (NSUInteger idx = 0; idx < size - 1; idx++)
286 {
287 /* Main parsing loop. If, at the end of the loop, replacement != nil,
288 we copy the characters from copyRangeStart to idx into the result,
289 the insert replacement, and skip replaceLength characters forward
290 (minus one, because idx is incremented by the loop.)
291 */
292 NSString *replacement = nil;
293 NSUInteger replaceLength = 0;
294 unichar thisChar = characters[idx];
295
296 if (thisChar == '[')
297 {
298 replacement = ExpandKey(context, characters, size, idx, &replaceLength, sizeLimit, recursionLimit);
299 }
300 else if (thisChar == '%')
301 {
302 replacement = ExpandPercentEscape(context, characters, size, idx, &replaceLength);
303 }
304 else if (thisChar == ']')
305 {
306 SyntaxWarning(context, @"strings.expand.warning.unbalancedClosingBracket", @"%@", @"Unbalanced ] in string.");
307 }
308 else if (thisChar == '\\' && context->convertBackslashN)
309 {
310 if (characters[idx + 1] == 'n')
311 {
312 replaceLength = 2;
313 replacement = @"\n";
314 }
315 }
316 else
317 {
318 // No token start character, so we definitely have no replacement.
319 continue;
320 }
321
322 if (replacement != nil)
323 {
324 /* If replacement string is "\x7F", eat the following character.
325 This is used in system_description for the one empty string
326 in [22].
327 */
328 if ([replacement isEqualToString:@"\x7F"] && replaceLength < size)
329 {
330 replaceLength++;
331 replacement = @"";
332 }
333
334 // Avoid copying if we're replacing the entire input string.
335 if (copyRangeStart == 0 && replaceLength == size)
336 {
337 return replacement;
338 }
339
340 // Write the pending literal segment to result. This also allocates result if needed.
341 AppendCharacters(&result, characters, copyRangeStart, idx);
342
343 [result appendString:replacement];
344
345 // Skip over replaced part and start a new literal segment.
346 idx += replaceLength - 1;
347 copyRangeStart = idx + 1;
348 }
349 }
350
351 if (result != nil)
352 {
353 // Append any trailing literal segment.
354 AppendCharacters(&result, characters, copyRangeStart, size);
355
356 // Don't turn result immutable; doing it once at top level is sufficient.
357 return result;
358 }
359 else
360 {
361 // No substitutions, return original string.
362 return string;
363 }
364}
365
366
367/* ExpandKey(context, characters, size, idx, replaceLength, sizeLimit, recursionLimit)
368
369 Expand a substitution key, i.e. a section surrounded by square brackets.
370 On entry, <idx> is the offset to an opening bracket. ExpandKey() searches
371 for the balancing closing bracket, and if it is found dispatches to either
372 ExpandDigitKey() (for a key consisting only of digits) or ExpandStringKey()
373 (for anything else).
374
375 The key may be terminated by a vertical bar |, followed by an operator. An
376 operator is an identifier, optionally followed by a colon and additional
377 text, and may be terminated with another bar and operator.
378*/
379static NSString *ExpandKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength, NSUInteger sizeLimit, NSUInteger recursionLimit)
380{
381 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
382 NSCParameterAssert(characters[idx] == '[');
383
384 // Find the balancing close bracket.
385 NSUInteger end, balanceCount = 1, firstBar = 0;
386 bool allDigits = true;
387
388 for (end = idx + 1; end < size && balanceCount > 0; end++)
389 {
390 if (characters[end] == ']') balanceCount--;
391 else
392 {
393 if (characters[end] == '[') balanceCount++;
394 else if (characters[end] == '|' && firstBar == 0) firstBar = end;
395 if (!isdigit(characters[end]) && firstBar == 0) allDigits = false;
396 }
397 }
398
399 // Fail if no balancing bracket.
400 if (EXPECT_NOT(balanceCount != 0))
401 {
402 SyntaxWarning(context, @"strings.expand.warning.unbalancedOpeningBracket", @"%@", @"Unbalanced [ in string.");
403 return nil;
404 }
405
406 NSUInteger totalLength = end - idx;
407 *replaceLength = totalLength;
408 NSUInteger keyStart = idx + 1, keyLength = totalLength - 2;
409 if (firstBar != 0) keyLength = firstBar - idx - 1;
410
411 if (EXPECT_NOT(keyLength == 0))
412 {
413 SyntaxWarning(context, @"strings.expand.warning.emptyKey", @"%@", @"Invalid expansion code [] string. (To avoid this message, use %%[%%].)");
414 return nil;
415 }
416
417 NSString *expanded = nil;
418 if (allDigits)
419 {
420 expanded = ExpandDigitKey(context, characters, keyStart, keyLength, sizeLimit, recursionLimit);
421 }
422 else
423 {
424 NSString *key = [NSString stringWithCharacters:characters + keyStart length:keyLength];
425 expanded = ExpandStringKey(context, key, sizeLimit, recursionLimit);
426 }
427
428 if (firstBar != 0)
429 {
430 NSString *operators = [NSString stringWithCharacters:characters + firstBar + 1 length:end - firstBar - 2];
431 expanded = ApplyOperators(expanded, operators);
432 }
433
434 return expanded;
435}
436
437
438/* ApplyOperators(string, operatorsString)
439
440 Given a string and a series of formatting operators separated by vertical
441 bars (or a single formatting operator), apply the operators, in sequence,
442 to the string.
443 */
444static NSString *ApplyOperators(NSString *string, NSString *operatorsString)
445{
446 NSArray *operators = [operatorsString componentsSeparatedByString:@"|"];
447 NSString *op = nil;
448
449 foreach(op, operators)
450 {
451 NSString *param = nil;
452 NSRange colon = [op rangeOfString:@":"];
453 if (colon.location != NSNotFound)
454 {
455 param = [op substringFromIndex:colon.location + colon.length];
456 op = [op substringToIndex:colon.location];
457 }
458 string = ApplyOneOperator(string, op, param);
459 }
460
461 return string;
462}
463
464
465static NSString *Operator_cr(NSString *string, NSString *param)
466{
467 return OOCredits([string doubleValue] * 10);
468}
469
470
471static NSString *Operator_dcr(NSString *string, NSString *param)
472{
473 return OOCredits([string longLongValue]);
474}
475
476
477static NSString *Operator_icr(NSString *string, NSString *param)
478{
479 return OOIntCredits([string longLongValue]);
480}
481
482
483static NSString *Operator_idcr(NSString *string, NSString *param)
484{
485 return OOIntCredits(round([string doubleValue] / 10.0));
486}
487
488
489static NSString *Operator_precision(NSString *string, NSString *param)
490{
491 return [NSString stringWithFormat:@"%.*f", [param intValue], [string doubleValue]];
492}
493
494
495static NSString *Operator_multiply(NSString *string, NSString *param)
496{
497 return [NSString stringWithFormat:@"%g", [string doubleValue] * [param doubleValue]];
498}
499
500
501static NSString *Operator_add(NSString *string, NSString *param)
502{
503 return [NSString stringWithFormat:@"%g", [string doubleValue] + [param doubleValue]];
504}
505
506
507/* ApplyOneOperator(string, op, param)
508
509 Apply a single formatting operator to a string.
510
511 For example, the expansion expression "[distance|precision:1]" will be
512 expanded by a call to ApplyOneOperator(@"distance", @"precision", @"1").
513
514 <param> may be nil, indicating an operator with no parameter (no colon).
515 */
516static NSString *ApplyOneOperator(NSString *string, NSString *op, NSString *param)
517{
518 static NSDictionary *operators = nil;
519
520 if (operators == nil)
521 {
522 #define OPERATOR(name) [NSValue valueWithPointer:Operator_##name], @#name
523 operators = [[NSDictionary alloc] initWithObjectsAndKeys:
524 OPERATOR(dcr),
525 OPERATOR(cr),
526 OPERATOR(icr),
527 OPERATOR(idcr),
528 OPERATOR(precision),
529 OPERATOR(multiply),
530 OPERATOR(add),
531 nil];
532 }
533
534 NSString *(*operator)(NSString *string, NSString *param) = [[operators objectForKey:op] pointerValue];
535 if (operator != NULL)
536 {
537 return operator(string, param);
538 }
539
540 OOLogERR(@"strings.expand.invalidOperator", @"Unknown string expansion operator %@", op);
541 return string;
542}
543
544
545/* ExpandDigitKey(context, characters, keyStart, keyLength, sizeLimit, recursionLimit)
546
547 Expand a key (as per ExpandKey()) consisting entirely of digits. <keyStart>
548 and <keyLength> specify the range of characters containing the key.
549
550 Digit-only keys are looked up in the system_description array in
551 descriptions.plist, which is expected to contain only arrays of strings (no
552 loose strings). When an array is retrieved, a string is selected from it
553 at random and the result is expanded recursively by calling Expand().
554*/
555static NSString *ExpandDigitKey(OOStringExpansionContext *context, const unichar *characters, NSUInteger keyStart, NSUInteger keyLength, NSUInteger sizeLimit, NSUInteger recursionLimit)
556{
557 NSCParameterAssert(context != NULL && characters != NULL);
558
559 NSUInteger keyValue = 0, idx;
560 for (idx = keyStart; idx < (keyStart + keyLength); idx++)
561 {
562 NSCAssert2(isdigit(characters[idx]), @"%s called with non-numeric key [%@].", __FUNCTION__, [NSString stringWithCharacters:characters + keyStart length:keyLength]);
563
564 keyValue = keyValue * 10 + characters[idx] - '0';
565 }
566
567 // Retrieve selected system_description entry.
568 NSArray *sysDescs = GetSystemDescriptions(context);
569 NSArray *entry = [sysDescs oo_arrayAtIndex:keyValue];
570
571 if (EXPECT_NOT(entry == nil))
572 {
573 if (keyValue >= context->sysDescCount)
574 {
575 SyntaxWarning(context, @"strings.expand.warning.outOfRangeKey", @"Out-of-range system description expansion key [%@] in string.", [NSString stringWithCharacters:characters + keyStart length:keyLength]);
576 }
577 else
578 {
579 // This is out of the scope of whatever triggered it, so shouldn't be a JS warning.
580 OOLogERR(@"strings.expand.invalidData", @"%@", @"descriptions.plist entry system_description must be an array of arrays of strings.");
581 }
582 return nil;
583 }
584
585 // Select a random sub-entry.
586 NSUInteger selection, count = [entry count];
587 NSUInteger rnd = OO_EXPANDER_RANDOM;
588 if (count == 5 && !context->useGoodRNG)
589 {
590 // Time-honoured Elite-compatible way for five items.
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;
595 else selection = 0;
596 }
597 else
598 {
599 // General way.
600 selection = (rnd * count) / 256;
601 }
602
603 // Look up and recursively expand string.
604 NSString *string = [entry oo_stringAtIndex:selection];
605 return Expand(context, string, sizeLimit, recursionLimit);
606}
607
608
609/* ExpandStringKey(context, key, sizeLimit, recursionLimit)
610
611 Expand a key (as per ExpandKey()) which doesn't consist entirely of digits.
612 Looks for the key in a number of different places in prioritized order.
613*/
614static NSString *ExpandStringKey(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit)
615{
616 NSCParameterAssert(context != NULL && key != nil);
617
618 // Overrides have top priority.
619 NSString *result = ExpandStringKeyOverride(context, key);
620
621 // Specials override descriptions.plist.
622 if (result == nil) result = ExpandStringKeySpecial(context, key);
623
624 // Now try descriptions.plist.
625 if (result == nil) result = ExpandStringKeyFromDescriptions(context, key, sizeLimit, recursionLimit);
626
627 // For efficiency, descriptions.plist overrides keybindings.
628 // OXPers should therefore avoid oolite_key_ description keys
629 if (result == nil) result = ExpandStringKeyKeyboardBinding(context, key);
630
631 // Try mission variables.
632 if (result == nil) result = ExpandStringKeyMissionVariable(context, key);
633
634 // Try legacy local variables.
635 if (result == nil) result = ExpandStringKeyLegacyLocalVariable(context, key);
636
637 // Try legacy script methods.
638 if (result == nil) ExpandLegacyScriptSelectorKey(context, key);
639
640#if WARNINGS
641 // None of that worked, so moan a bit.
642 if (result == nil) ReportWarningForUnknownKey(context, key);
643#endif
644
645 return result;
646}
647
648
649/* ExpandStringKeyOverride(context, key)
650
651 Attempt to expand a key by retriving it from the overrides dictionary of
652 the context (ultimately from OOExpandDescriptionString()). Overrides are
653 used to provide context-specific expansions, such as "[self:name]" in
654 comms messages, and can also be used from JavaScript.
655
656 The main difference between overrides and legacy locals is priority.
657*/
658static NSString *ExpandStringKeyOverride(OOStringExpansionContext *context, NSString *key)
659{
660 NSCParameterAssert(context != NULL && key != nil);
661
662 id value = [context->overrides objectForKey:key];
663 if (value != nil)
664 {
665#if WARNINGS
666 if (![value isKindOfClass:[NSString class]] && ![value isKindOfClass:[NSNumber class]])
667 {
668 SyntaxWarning(context, @"strings.expand.warning.invalidOverride", @"String expansion override value %@ for [%@] is not a string or number.", [value shortDescription], key);
669 }
670#endif
671 return [value description];
672 }
673
674 return nil;
675}
676
677
678/* ExpandStringKeySpecial(context, key)
679
680 Attempt to expand a key by matching a set of special expansion codes that
681 call PlayerEntity methods but aren't legacy script methods. Also unlike
682 legacy script methods, all these methods return strings.
683*/
684static NSString *ExpandStringKeySpecial(OOStringExpansionContext *context, NSString *key)
685{
686 NSCParameterAssert(context != NULL && key != nil);
687
688 NSMapTable *specials = SpecialSubstitutionSelectors();
689 SEL selector = NSMapGet(specials, key);
690 if (selector != NULL)
691 {
692 NSCAssert2([PLAYER respondsToSelector:selector], @"Special string expansion selector %@ for [%@] is not implemented.", NSStringFromSelector(selector), key);
693
694 NSString *result = [PLAYER performSelector:selector];
695 if (result != nil)
696 {
697 NSCAssert2([result isKindOfClass:[NSString class]], @"Special string expansion [%@] expanded to %@, but expected a string.", key, [result shortDescription]);
698 return result;
699 }
700 }
701
702 return nil;
703}
704
705
706/* ExpandStringKeyKeyboardBinding(context, key)
707
708 Attempt to expand a key by matching it against the keybindings
709*/
710static NSString *ExpandStringKeyKeyboardBinding(OOStringExpansionContext *context, NSString *key)
711{
712 NSCParameterAssert(context != NULL && key != nil);
713 if ([key hasPrefix:@"oolite_key_"])
714 {
715 NSString *binding = [key substringFromIndex:7];
716 return [PLAYER keyBindingDescription2:binding];
717 }
718 return nil;
719}
720
721
722/* SpecialSubstitutionSelectors()
723
724 Retrieve the mapping of special keys for ExpandStringKeySpecial() to
725 selectors.
726*/
727static NSMapTable *SpecialSubstitutionSelectors(void)
728{
729 static NSMapTable *specials = NULL;
730 if (specials != NULL) return specials;
731
732 struct { NSString *key; SEL selector; } selectors[] =
733 {
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) }
743 };
744 unsigned i, count = sizeof selectors / sizeof *selectors;
745
746 specials = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, count);
747 for (i = 0; i < count; i++)
748 {
749 NSMapInsertKnownAbsent(specials, selectors[i].key, selectors[i].selector);
750 }
751
752 return specials;
753}
754
755
756/* ExpandStringKeyFromDescriptions(context, key, sizeLimit, recursionLimit)
757
758 Attempt to expand a key by looking it up in descriptions.plist. Matches
759 may be single strings or arrays of strings. For arrays, one of the strings
760 is selected at random.
761
762 Matched strings are expanded recursively by calling Expand().
763*/
764static NSString *ExpandStringKeyFromDescriptions(OOStringExpansionContext *context, NSString *key, NSUInteger sizeLimit, NSUInteger recursionLimit)
765{
766 id value = [[UNIVERSE descriptions] objectForKey:key];
767 if (value != nil)
768 {
769 if ([value isKindOfClass:[NSArray class]] && [value count] > 0)
770 {
771 NSUInteger rnd = OO_EXPANDER_RANDOM % [value count];
772 value = [value oo_objectAtIndex:rnd];
773 }
774
775 if (![value isKindOfClass:[NSString class]])
776 {
777 // This is out of the scope of whatever triggered it, so shouldn't be a JS warning.
778 OOLogERR(@"strings.expand.invalidData", @"String expansion value %@ for [%@] from descriptions.plist is not a string or number.", [value shortDescription], key);
779 return nil;
780 }
781
782 // Expand recursively.
783 return Expand(context, value, sizeLimit, recursionLimit);
784 }
785
786 return nil;
787}
788
789
790/* ExpandStringKeyMissionVariable(context, key)
791
792 Attempt to expand a key by matching it to a mission variable.
793*/
794static NSString *ExpandStringKeyMissionVariable(OOStringExpansionContext *context, NSString *key)
795{
796 if ([key hasPrefix:@"mission_"])
797 {
798 return [PLAYER missionVariableForKey:key];
799 }
800
801 return nil;
802}
803
804
805/* ExpandStringKeyMissionVariable(context, key)
806
807 Attempt to expand a key by matching it to a legacy local variable.
808
809 The main difference between overrides and legacy locals is priority.
810*/
811static NSString *ExpandStringKeyLegacyLocalVariable(OOStringExpansionContext *context, NSString *key)
812{
813 return [[context->legacyLocals objectForKey:key] description];
814}
815
816
817/* ExpandLegacyScriptSelectorKey(context, key)
818
819 Attempt to expand a key by treating it as a legacy script query method and
820 invoking it. Only whitelisted methods are permitted, and aliases are
821 respected.
822*/
823static NSString *ExpandLegacyScriptSelectorKey(OOStringExpansionContext *context, NSString *key)
824{
825 NSCParameterAssert(context != NULL && key != nil);
826
827 SEL selector = LookUpLegacySelector(key);
828
829 if (selector != NULL)
830 {
831 return [[PLAYER performSelector:selector] description];
832 }
833 else
834 {
835 return nil;
836 }
837}
838
839
840/* LookUpLegacySelector(key)
841
842 If <key> is a whitelisted legacy script query method, or aliases to one,
843 return the corresponding selector.
844*/
845static SEL LookUpLegacySelector(NSString *key)
846{
847 SEL selector = NULL;
848 static NSMapTable *selectorCache = NULL;
849
850 // Try cache lookup.
851 if (selectorCache != NULL)
852 {
853 selector = NSMapGet(selectorCache, key);
854 }
855
856 if (selector == NULL)
857 {
858 static NSDictionary *aliases = nil;
859 static NSSet *whitelist = nil;
860 if (whitelist == nil)
861 {
862 NSDictionary *whitelistDict = [ResourceManager whitelistDictionary];
863 whitelist = [[NSSet alloc] initWithArray:[whitelistDict oo_arrayForKey:@"query_methods"]];
864 aliases = [[whitelistDict oo_dictionaryForKey:@"query_method_aliases"] copy];
865 }
866
867 NSString *selectorName = [aliases oo_stringForKey:key];
868 if (selectorName == nil) selectorName = key;
869
870 if ([whitelist containsObject:selectorName])
871 {
872 selector = NSSelectorFromString(selectorName);
873
874 /* This is an assertion, not a warning, because whitelist.plist is
875 part of the game and cannot be overriden by OXPs. If there is an
876 invalid selector in the whitelist, it's a game bug.
877 */
878 NSCAssert1([PLAYER respondsToSelector:selector], @"Player does not respond to whitelisted query selector %@.", key);
879 }
880
881 if (selector != NULL)
882 {
883 // Add it to cache.
884 if (selectorCache == NULL)
885 {
886 selectorCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, [whitelist count]);
887 }
888 NSMapInsertKnownAbsent(selectorCache, key, selector);
889 }
890 }
891
892 return selector;
893}
894
895
896#if WARNINGS
897/* ReportWarningForUnknownKey(context, key)
898
899 Called when we fall through all the various ways of expanding string keys
900 above. If the key looks like a legacy script query method, assume it is
901 and report a bad selector. Otherwise, report it as an unknown key.
902*/
903static void ReportWarningForUnknownKey(OOStringExpansionContext *context, NSString *key)
904{
905 if ([key hasSuffix:@"_string"] || [key hasSuffix:@"_number"] || [key hasSuffix:@"_bool"])
906 {
907 SyntaxError(context, @"strings.expand.invalidSelector", @"Unpermitted legacy script method [%@] in string.", key);
908 }
909 else
910 {
911 SyntaxWarning(context, @"strings.expand.warning.unknownExpansion", @"Unknown expansion key [%@] in string.", key);
912 }
913}
914#endif
915
916
917/* ExpandKey(context, characters, size, idx, replaceLength)
918
919 Expand an escape code. <idx> is the index of the % sign introducing the
920 escape code. Supported escape codes are:
921 %H
922 %I
923 %N
924 %R
925 %J###, where ### are three digits
926 %G######, where ### are six digits
927 %%
928 %[
929 %]
930
931 In addition, the codes %@, %d and %. are ignored, because they're used
932 with -[NSString stringWithFormat:] on strings that have already been
933 expanded.
934
935 Any other code results in a warning.
936*/
937static NSString *ExpandPercentEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
938{
939 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
940 NSCParameterAssert(characters[idx] == '%');
941
942 // All %-escapes except %J and %G are 2 characters.
943 *replaceLength = 2;
944 unichar selector = characters[idx + 1];
945
946 switch (selector)
947 {
948 case 'H':
949 return GetSystemName(context);
950
951 case 'I':
952 return GetSystemNameIan(context);
953
954 case 'N':
955 return GetRandomNameN(context);
956
957 case 'R':
958 // to keep planet description generation consistent with earlier versions
959 // this must be done after all other substitutions in a second pass.
960 context->hasPercentR = true;
961 return @"%R";
962
963 case 'G':
964 return ExpandSystemNameForGalaxyEscape(context, characters, size, idx, replaceLength);
965
966 case 'J':
967 return ExpandSystemNameEscape(context, characters, size, idx, replaceLength);
968
969 case '%':
970 return @"%";
971
972 case '[':
973 return @"[";
974
975 case ']':
976 return @"]";
977
978 /* These are NSString formatting specifiers that occur in
979 descriptions.plist. The '.' is for floating-point (g and f)
980 specifiers that have field widths specified. No unadorned
981 %f or %g is found in vanilla Oolite descriptions.plist.
982
983 Ideally, these would be replaced with the caller formatting
984 the value and passing it as an override - it would be safer
985 and make descriptions.plist clearer - but it would be a big
986 job and uglify the callers without newfangled Objective-C
987 dictionary literals.
988 -- Ahruman 2012-10-05
989 */
990 case '@':
991 case 'd':
992 case '.':
993 return nil;
994
995 default:
996 // Yay, percent signs!
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);
998
999 return nil;
1000 }
1001}
1002
1003
1004/* ExpandPercentR(context, string)
1005 Replaces all %R in string with its expansion.
1006 Separate to allow this to be delayed to the end of the string expansion
1007 for compatibility with 1.76 expansion of %R in planet descriptions
1008*/
1009static NSString *ExpandPercentR(OOStringExpansionContext *context, NSString *input)
1010{
1011 NSRange containsR = [input rangeOfString:@"%R"];
1012 if (containsR.location == NSNotFound)
1013 {
1014 return input; // no %Rs to replace
1015 }
1016 NSString *percentR = GetRandomNameR(context);
1017 NSMutableString *output = [NSMutableString stringWithString:input];
1018
1019 /* This loop should be completely unnecessary, but for some reason
1020 * replaceOccurrencesOfString sometimes only replaces the first
1021 * instance of %R if percentR contains the non-ASCII
1022 * digrams-apostrophe character. (I guess
1023 * http://lists.gnu.org/archive/html/gnustep-dev/2011-10/msg00048.html
1024 * this bug in GNUstep's implementation here, which is in 1.22) So
1025 * to cover that case, if there are still %R in the string after
1026 * replacement, try again. Affects things like thargoid curses, and
1027 * particularly %Rful expansions of [nom]. Probably this can be
1028 * tidied up once GNUstep 1.22 is ancient history, but that'll be a
1029 * few years yet. - CIM 15/1/2013 */
1030
1031 do {
1032 [output replaceOccurrencesOfString:@"%R" withString:percentR options:NSLiteralSearch range:NSMakeRange(0, [output length])];
1033 } while([output rangeOfString:@"%R"].location != NSNotFound);
1034
1035 return [NSString stringWithString:output];
1036}
1037
1038
1039/* ExpandSystemNameForGalaxyEscape(context, characters, size, idx, replaceLength)
1040
1041 Expand a %G###### code by looking up the corresponding system name in any
1042 cgalaxy.
1043*/
1044static NSString *ExpandSystemNameForGalaxyEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
1045{
1046 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
1047 NSCParameterAssert(characters[idx + 1] == 'G');
1048
1049 // A valid %G escape is always eight characters including the six digits.
1050 *replaceLength = 8;
1051
1052 #define kInvalidGEscapeMessage @"String escape code %G must be followed by six integers."
1053 if (EXPECT_NOT(size - idx < 8))
1054 {
1055 // Too close to end of string to actually have six characters, let alone six digits.
1056 SyntaxError(context, @"strings.expand.invalidJEscape", @"%@", kInvalidGEscapeMessage);
1057 return nil;
1058 }
1059
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];
1066
1067 if (!(isdigit(hundreds) && isdigit(tens) && isdigit(units) && isdigit(galHundreds) && isdigit(galTens) && isdigit(galUnits)))
1068 {
1069 SyntaxError(context, @"strings.expand.invalidJEscape", @"%@", kInvalidGEscapeMessage);
1070 return nil;
1071 }
1072
1073 OOSystemID sysID = (hundreds - '0') * 100 + (tens - '0') * 10 + (units - '0');
1074 if (sysID > kOOMaximumSystemID)
1075 {
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);
1077 return nil;
1078 }
1079
1080 OOGalaxyID galID = (galHundreds - '0') * 100 + (galTens - '0') * 10 + (galUnits - '0');
1081 if (galID > kOOMaximumGalaxyID)
1082 {
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);
1084 return nil;
1085 }
1086
1087 return [UNIVERSE getSystemName:sysID forGalaxy:galID];
1088}
1089
1090
1091/* ExpandSystemNameEscape(context, characters, size, idx, replaceLength)
1092
1093 Expand a %J### code by looking up the corresponding system name in the
1094 current galaxy.
1095*/
1096static NSString *ExpandSystemNameEscape(OOStringExpansionContext *context, const unichar *characters, NSUInteger size, NSUInteger idx, NSUInteger *replaceLength)
1097{
1098 NSCParameterAssert(context != NULL && characters != NULL && replaceLength != NULL);
1099 NSCParameterAssert(characters[idx + 1] == 'J');
1100
1101 // A valid %J escape is always five characters including the three digits.
1102 *replaceLength = 5;
1103
1104 #define kInvalidJEscapeMessage @"String escape code %J must be followed by three integers."
1105 if (EXPECT_NOT(size - idx < 5))
1106 {
1107 // Too close to end of string to actually have three characters, let alone three digits.
1108 SyntaxError(context, @"strings.expand.invalidJEscape", @"%@", kInvalidJEscapeMessage);
1109 return nil;
1110 }
1111
1112 char hundreds = characters[idx + 2];
1113 char tens = characters[idx + 3];
1114 char units = characters[idx + 4];
1115
1116 if (!(isdigit(hundreds) && isdigit(tens) && isdigit(units)))
1117 {
1118 SyntaxError(context, @"strings.expand.invalidJEscape", @"%@", kInvalidJEscapeMessage);
1119 return nil;
1120 }
1121
1122 OOSystemID sysID = (hundreds - '0') * 100 + (tens - '0') * 10 + (units - '0');
1123 if (sysID > kOOMaximumSystemID)
1124 {
1125 SyntaxError(context, @"strings.expand.invalidJEscape.range", @"String escape code %%J%3u is out of range (must be less than %u).", sysID, kOOMaximumSystemID + 1);
1126 return nil;
1127 }
1128
1129 return [UNIVERSE getSystemName:sysID];
1130}
1131
1132
1133static void AppendCharacters(NSMutableString **result, const unichar *characters, NSUInteger start, NSUInteger end)
1134{
1135 NSCParameterAssert(result != NULL && characters != NULL && start <= end);
1136
1137 if (*result == nil)
1138 {
1139 // Ensure there is a string. We want this even if the range is empty.
1140 *result = [NSMutableString string];
1141 }
1142
1143 if (start == end) return;
1144
1145 /* What we want here is a method like -[NSMutableString
1146 appendCharacters:(unichar)characters length:(NSUInteger)length], which
1147 unfortunately doesn't exist. On Mac OS X, CoreFoundation provides an
1148 equivalent. For GNUstep, we have to use a temporary string.
1149
1150 TODO: build the output string in a fixed-size stack buffer instead.
1151 */
1152#if OOLITE_MAC_OS_X
1153 CFStringAppendCharacters((CFMutableStringRef)*result, characters + start, end - start);
1154#else
1155 NSString *temp = [[NSString alloc] initWithCharacters:characters + start length:end - start];
1156 [*result appendString:temp];
1157 [temp release];
1158#endif
1159}
1160
1161
1162static NSString *GetSystemName(OOStringExpansionContext *context)
1163{
1164 NSCParameterAssert(context != NULL);
1165 if (context->systemName == nil) {
1166 context->systemName = [[UNIVERSE getSystemName:[PLAYER systemID]] retain];
1167 }
1168
1169 return context->systemName;
1170}
1171
1172
1174{
1175 NSCParameterAssert(context != NULL);
1176
1177 if (context->systemNameWithIan == nil)
1178 {
1179 context->systemNameWithIan = [OOExpandWithOptions(context->seed, kOOExpandDisallowPercentI | kOOExpandGoodRNG | kOOExpandKey, @"planetname-possessive") retain];
1180 }
1181
1182 return context->systemNameWithIan;
1183}
1184
1185
1187{
1188 NSCParameterAssert(context != NULL);
1189
1190 if (context->randomNameN == nil)
1191 {
1192 context->randomNameN = [NewRandomDigrams(context) retain];
1193 }
1194
1195 return context->randomNameN;
1196}
1197
1198
1200{
1201 NSCParameterAssert(context != NULL);
1202
1203 if (context->randomNameR == nil)
1204 {
1205 context->randomNameR = [OldRandomDigrams() retain];
1206 }
1207
1208 return context->randomNameR;
1209}
1210
1211
1213{
1214 NSCParameterAssert(context != NULL);
1215
1216 if (context->systemDescriptions == nil)
1217 {
1218 context->systemDescriptions = [[[UNIVERSE descriptions] oo_arrayForKey:@"system_description"] retain];
1219 context->sysDescCount = [context->systemDescriptions count];
1220 }
1221
1222 return context->systemDescriptions;
1223}
1224
1225
1226/* Generates pseudo-random digram string using gen_rnd_number()
1227 (world-generation consistent PRNG), but misses some possibilities. Used
1228 for "%R" description string for backwards compatibility.
1229*/
1230static NSString *OldRandomDigrams(void)
1231{
1232 /* The only point of using %R is for world generation, so there's
1233 * no point in checking the context */
1234 unsigned len = gen_rnd_number() & 3;
1235 NSString *digrams = [[UNIVERSE descriptions] objectForKey:@"digrams"];
1236 NSMutableString *name = [NSMutableString stringWithCapacity:256];
1237
1238 for (unsigned i = 0; i <=len; i++)
1239 {
1240 unsigned x = gen_rnd_number() & 0x3e;
1241 [name appendString:[digrams substringWithRange:NSMakeRange(x, 2)]];
1242 }
1243
1244 return [name capitalizedString];
1245}
1246
1247
1248/* Generates pseudo-random digram string. Used for "%N" description string.
1249*/
1251{
1252 unsigned length = (OO_EXPANDER_RANDOM % 4) + 1;
1253 if ((OO_EXPANDER_RANDOM % 5) < ((length == 1) ? 3 : 1)) ++length; // Make two-letter names rarer and 10-letter names happen sometimes
1254 NSString *digrams = [[UNIVERSE descriptions] objectForKey:@"digrams"];
1255 NSUInteger count = [digrams length] / 2;
1256 NSMutableString *name = [NSMutableString stringWithCapacity:length * 2];
1257
1258 for (unsigned i = 0; i != length; ++i)
1259 {
1260 [name appendString:[digrams substringWithRange:NSMakeRange((OO_EXPANDER_RANDOM % count) * 2, 2)]];
1261 }
1262
1263 return [name capitalizedString];
1264}
1265
1266
1267static void SyntaxIssue(OOStringExpansionContext *context, const char *function, const char *fileName, NSUInteger line, NSString *logMessageClass, NSString *prefix, NSString *format, ...)
1268{
1269 NSCParameterAssert(context != NULL);
1270
1271 va_list args;
1272 va_start(args, format);
1273
1274 if (OOLogWillDisplayMessagesInClass(logMessageClass))
1275 {
1276 if (context->isJavaScript)
1277 {
1278 /* NOTE: syntax errors are reported as warnings when called from JS
1279 because we don't want to start throwing exceptions when the old
1280 expander didn't.
1281 */
1282 JSContext *jsc = OOJSAcquireContext();
1283 OOJSReportWarningWithArguments(jsc, format, args);
1285 }
1286 else
1287 {
1288 format = [prefix stringByAppendingString:format];
1289 OOLogWithFunctionFileAndLineAndArguments(logMessageClass, function, fileName, line, format, args);
1290 }
1291 }
1292
1293 va_end(args);
1294}
#define EXPECT_NOT(x)
#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,...)
Definition OOLogging.h:112
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)
Definition OOLogging.m:144
unsigned count
return nil
float x
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)
@ kRecursionLimit
@ kStackAllocationLimit
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)
int16_t OOSystemID
Definition OOTypes.h:211
uint8_t OOGalaxyID
Definition OOTypes.h:210
@ kOOMaximumSystemID
Definition OOTypes.h:217
@ kOOMaximumGalaxyID
Definition OOTypes.h:216
#define PLAYER
NSDictionary * whitelistDictionary()
voidpf void uLong size
Definition ioapi.h:134
void seed_RNG_only_for_planet_description(Random_Seed s_seed)
void OORestoreRandomState(OORandomState state)
OORandomState OOSaveRandomState(void)
int gen_rnd_number(void)
void OOSetReallyRandomRANROTAndRndSeeds(void)