void SBarInfo::ParseMugShotBlock(FScanner &sc, FMugShotState &state) { sc.MustGetToken('{'); while(!sc.CheckToken('}')) { FMugShotFrame frame; bool multiframe = false; if(sc.CheckToken('{')) multiframe = true; do { sc.MustGetToken(TK_Identifier); if(strlen(sc.String) > 5) sc.ScriptError("MugShot frames cannot exceed 5 characters."); frame.Graphic.Push(sc.String); } while(multiframe && sc.CheckToken(',')); if(multiframe) sc.MustGetToken('}'); bool negative = sc.CheckToken('-'); sc.MustGetToken(TK_IntConst); frame.Delay = (negative ? -1 : 1)*sc.Number; sc.MustGetToken(';'); state.Frames.Push(frame); } }
static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) { int currvalue = 0; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('=')) { FxExpression *expr = ParseExpression (sc, cls); currvalue = expr->EvalExpression(NULL).GetInt(); delete expr; } PSymbolConst *sym = new PSymbolConst(symname); sym->ValueType = VAL_Int; sym->Value = currvalue; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; sc.MustGetToken(','); currvalue++; } sc.MustGetToken(';'); }
static void ParseActionDef (FScanner &sc, PClassActor *cls) { unsigned int error = 0; FName funcname; TArray<PType *> rets; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("Action functions can only be imported by internal class and actor definitions!"); FScriptPosition::ErrorCounter++; } sc.MustGetToken(TK_Native); // check for a return value do { if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool)) { rets.Push(TypeSInt32); } else if (sc.CheckToken(TK_State)) { rets.Push(TypeState); } else if (sc.CheckToken(TK_Float)) { rets.Push(TypeFloat64); } } while (sc.CheckToken(',')); sc.MustGetToken(TK_Identifier); funcname = sc.String; ParseFunctionDef(sc, cls, funcname, rets, VARF_Method | VARF_Action); }
static FString ParseMultiString(FScanner &scanner, int error) { FString build; if (scanner.CheckToken(TK_Identifier)) { if (!stricmp(scanner.String, "clear")) { return "-"; } else { scanner.ScriptError("Either 'clear' or string constant expected"); } } do { scanner.MustGetToken(TK_StringConst); if (build.Len() > 0) build += "\n"; build += scanner.String; } while (scanner.CheckToken(',')); return build; }
void GetCoordinates(FScanner &sc, bool fullScreenOffsets, SBarInfoCoordinate &x, SBarInfoCoordinate &y) { bool negative = false; bool relCenter = false; SBarInfoCoordinate *coords[2] = {&x, &y}; for(int i = 0;i < 2;i++) { negative = false; relCenter = false; if(i > 0) sc.MustGetToken(','); // [-]INT center negative = sc.CheckToken('-'); sc.MustGetToken(TK_IntConst); coords[i]->Set(negative ? -sc.Number : sc.Number, false); if(sc.CheckToken('+')) { sc.MustGetToken(TK_Identifier); if(!sc.Compare("center")) sc.ScriptError("Expected 'center' but got '%s' instead.", sc.String); relCenter = true; } if(fullScreenOffsets) { coords[i]->SetRelCenter(relCenter); } } //if(!fullScreenOffsets) // y.SetCoord((negative ? -sc.Number : sc.Number) - (200 - script->height)); }
void SBarInfo::getCoordinates(FScanner &sc, bool fullScreenOffsets, int &x, int &y) { bool negative = false; bool relCenter = false; int *coords[2] = {&x, &y}; for(int i = 0;i < 2;i++) { negative = false; relCenter = false; if(i > 0) sc.MustGetToken(','); // [-]INT center negative = sc.CheckToken('-'); sc.MustGetToken(TK_IntConst); *coords[i] = negative ? -sc.Number : sc.Number; if(sc.CheckToken('+')) { sc.MustGetToken(TK_Identifier); if(!sc.Compare("center")) sc.ScriptError("Expected 'center' but got '%s' instead.", sc.String); relCenter = true; } if(fullScreenOffsets) { if(relCenter) *coords[i] |= SBarInfoCoordinate::REL_CENTER; else *coords[i] &= ~SBarInfoCoordinate::REL_CENTER; } } if(!fullScreenOffsets) y = (negative ? -sc.Number : sc.Number) - (200 - this->height); }
static void PSR_FindEndBlock(FScanner &sc) { int depth = 1; do { if(sc.CheckToken('}')) --depth; else if(sc.CheckToken('{')) ++depth; else sc.MustGetAnyToken(); } while(depth); }
static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword) { // Finds a block with a given keyword and then enter it (opening brace) // Should be closed with PSR_FindEndBlock while(sc.GetToken()) { if(sc.TokenType == '}') { sc.UnGet(); return false; } sc.TokenMustBe(TK_StringConst); if(!sc.Compare(keyword)) { if(!sc.CheckToken(TK_StringConst)) PSR_SkipBlock(sc); } else { sc.MustGetToken('{'); return true; } } return false; }
static TArray<FString> PSR_ReadBaseInstalls(FScanner &sc) { TArray<FString> result; // Get a list of possible install directories. while(sc.GetToken()) { if(sc.TokenType == '}') break; sc.TokenMustBe(TK_StringConst); FString key(sc.String); if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0) { sc.MustGetToken(TK_StringConst); result.Push(FString(sc.String) + "/steamapps/common"); } else { if(sc.CheckToken('{')) PSR_FindEndBlock(sc); else sc.MustGetToken(TK_StringConst); } } return result; }
static void ParseConstant (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { // Read the type and make sure it's int or float. if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) { int type = sc.TokenType; sc.MustGetToken(TK_Identifier); FName symname = sc.String; sc.MustGetToken('='); FxExpression *expr = ParseExpression (sc, cls, true); sc.MustGetToken(';'); if (!expr->isConstant()) { sc.ScriptMessage("Constant definition is not a constant"); FScriptPosition::ErrorCounter++; } else { ExpVal val = static_cast<FxConstant *>(expr)->GetValue(); delete expr; PSymbolConstNumeric *sym; if (type == TK_Int) { sym = new PSymbolConstNumeric(symname, TypeSInt32); sym->Value = val.GetInt(); } else { sym = new PSymbolConstNumeric(symname, TypeFloat64); sym->Float = val.GetFloat(); } if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } } } else { sc.ScriptMessage("Numeric type required for constant"); FScriptPosition::ErrorCounter++; } }
void gl_ParseHardwareShader(FScanner &sc, int deflump) { int type = FTexture::TEX_Any; bool disable_fullbright=false; bool thiswad = false; bool iwad = false; int maplump = -1; FString maplumpname; float speed = 1.f; sc.MustGetString(); if (sc.Compare("texture")) type = FTexture::TEX_Wall; else if (sc.Compare("flat")) type = FTexture::TEX_Flat; else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; else sc.UnGet(); sc.MustGetString(); FTextureID no = TexMan.CheckForTexture(sc.String, type); FTexture *tex = TexMan[no]; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetString(); if (sc.Compare("shader")) { sc.MustGetString(); maplumpname = sc.String; } else if (sc.Compare("speed")) { sc.MustGetFloat(); speed = float(sc.Float); } } if (!tex) { return; } if (maplumpname.IsNotEmpty()) { if (tex->bWarped != 0) { Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name.GetChars()); return; } tex->gl_info.shaderspeed = speed; for(unsigned i=0;i<usershaders.Size();i++) { if (!usershaders[i].CompareNoCase(maplumpname)) { tex->gl_info.shaderindex = i + FIRST_USER_SHADER; return; } } tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; } }
//========================================================================== //*** // DoActionSpecials // handles action specials as code pointers // //========================================================================== bool DoActionSpecials(FScanner &sc, FState & state, Baggage &bag) { int i; int min_args, max_args; FString specname = sc.String; int special = P_FindLineSpecial(sc.String, &min_args, &max_args); if (special > 0 && min_args >= 0) { int paramindex=PrepareStateParameters(&state, 6, bag.Info->Class); StateParams.Set(paramindex, new FxConstant(special, sc)); // Make this consistent with all other parameter parsing if (sc.CheckToken('(')) { for (i = 0; i < 5;) { StateParams.Set(paramindex+i+1, ParseExpression (sc, bag.Info->Class)); i++; if (!sc.CheckToken (',')) break; } sc.MustGetToken (')'); } else i=0; if (i < min_args) { sc.ScriptError ("Too few arguments to %s", specname.GetChars()); } if (i > max_args) { sc.ScriptError ("Too many arguments to %s", specname.GetChars()); } state.SetAction(FindGlobalActionFunction("A_CallSpecial"), false); return true; } return false; }
static int ParseMapEntry(FScanner &scanner, UMapEntry *val) { scanner.MustGetToken(TK_Identifier); val->MapName = scanner.String; scanner.MustGetToken('{'); while(!scanner.CheckToken('}')) { ParseStandardProperty(scanner, val); } return 1; }
static void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) { // Read the type and make sure it's int or float. if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) { int type = sc.TokenType; sc.MustGetToken(TK_Identifier); FName symname = sc.String; sc.MustGetToken('='); FxExpression *expr = ParseExpression (sc, cls); sc.MustGetToken(';'); ExpVal val = expr->EvalExpression(NULL); delete expr; PSymbolConst *sym = new PSymbolConst(symname); if (type == TK_Int) { sym->ValueType = VAL_Int; sym->Value = val.GetInt(); } else { sym->ValueType = VAL_Float; sym->Float = val.GetFloat(); } if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } } else { sc.ScriptMessage("Numeric type required for constant"); FScriptPosition::ErrorCounter++; } }
static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { int currvalue = 0; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('=')) { FxExpression *expr = ParseExpression (sc, cls, true); if (!expr->isConstant()) { sc.ScriptMessage("'%s' must be constant", symname.GetChars()); FScriptPosition::ErrorCounter++; } else { currvalue = static_cast<FxConstant *>(expr)->GetValue().GetInt(); } delete expr; } PSymbolConstNumeric *sym = new PSymbolConstNumeric(symname, TypeSInt32); sym->Value = currvalue; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; sc.MustGetToken(','); currvalue++; } sc.MustGetToken(';'); }
int SBarInfo::getSignedInteger(FScanner &sc) { if(sc.CheckToken('-')) { sc.MustGetToken(TK_IntConst); return -sc.Number; } else { sc.MustGetToken(TK_IntConst); return sc.Number; } }
EColorRange GetTranslation(FScanner &sc) { if (!sc.CheckToken(TK_Null)) sc.MustGetToken(TK_Identifier); EColorRange returnVal = CR_UNTRANSLATED; FString namedTranslation; //we must send in "[translation]" const BYTE *trans_ptr; namedTranslation.Format("[%s]", sc.String); trans_ptr = (const BYTE *)(&namedTranslation[0]); if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED) { sc.ScriptError("Missing definition for color %s.", sc.String); } return returnVal; }
void Parse(FScanner &sc, bool fullScreenOffsets) { this->fullScreenOffsets = fullScreenOffsets; if(sc.CheckToken(',')) { while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("forcescaled")) forceScaled = true; else if(sc.Compare("fullscreenoffsets")) this->fullScreenOffsets = true; else sc.ScriptError("Unkown flag '%s'.", sc.String); if(!sc.CheckToken('|') && !sc.CheckToken(',')) { SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets); return; } } sc.MustGetToken(TK_FloatConst); alpha = sc.Float; } SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets); }
void Parse(FScanner &sc, bool fullScreenOffsets) { bool negate = false; if(sc.CheckToken(TK_Identifier)) { if(sc.Compare("not")) negate = true; else sc.UnGet(); } ParseNegatable(sc, fullScreenOffsets); SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); if(negate) Negate(); }
void ParseBlock(TDeletingArray<SBarInfoCommand *> &commands, FScanner &sc, bool fullScreenOffsets) { if(sc.CheckToken('{')) { while(SBarInfoCommand *cmd = NextCommand(sc)) { cmd->Parse(sc, fullScreenOffsets); commands.Push(cmd); } } else { if(SBarInfoCommand *cmd = NextCommand(sc)) { cmd->Parse(sc, fullScreenOffsets); commands.Push(cmd); } else sc.ScriptError("Missing command for flow control statement."); } }
bool FIntermissionActionTextscreen::ParseKey(FScanner &sc) { if (sc.Compare("Position")) { sc.MustGetToken('='); sc.MustGetToken(TK_IntConst); mTextX = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); mTextY = sc.Number; return true; } else if (sc.Compare("TextLump")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); int lump = Wads.CheckNumForFullName(sc.String, true); if (lump > 0) { mText = Wads.ReadLump(lump).GetString(); } else { // only print an error if coming from a PWAD if (Wads.GetLumpFile(sc.LumpNum) > 1) sc.ScriptMessage("Unknown text lump '%s'", sc.String); mText.Format("Unknown text lump '%s'", sc.String); } return true; } else if (sc.Compare("Text")) { sc.MustGetToken('='); do { sc.MustGetToken(TK_StringConst); mText << sc.String << '\n'; } while (sc.CheckToken(',')); return true; } else if (sc.Compare("TextColor")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mTextColor = V_FindFontColor(sc.String); return true; } else if (sc.Compare("TextDelay")) { sc.MustGetToken('='); if (!sc.CheckToken('-')) { sc.MustGetFloat(); mTextDelay = int(sc.Float*TICRATE); } else { sc.MustGetToken(TK_IntConst); mTextDelay = sc.Number; } return true; } else if (sc.Compare("textspeed")) { sc.MustGetToken('='); sc.MustGetToken(TK_IntConst); mTextSpeed = sc.Number; return true; } else return Super::ParseKey(sc); }
static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) { // find the next line with content. // this line is no property. scanner.MustGetToken(TK_Identifier); FString pname = scanner.String; scanner.MustGetToken('='); if (!pname.CompareNoCase("levelname")) { scanner.MustGetToken(TK_StringConst); mape->LevelName = scanner.String; } else if (!pname.CompareNoCase("next")) { ParseLumpName(scanner, mape->nextmap); } else if (!pname.CompareNoCase("nextsecret")) { ParseLumpName(scanner, mape->nextsecret); } else if (!pname.CompareNoCase("levelpic")) { ParseLumpName(scanner, mape->levelpic); } else if (!pname.CompareNoCase("skytexture")) { ParseLumpName(scanner, mape->skytexture); } else if (!pname.CompareNoCase("music")) { ParseLumpName(scanner, mape->music); } else if (!pname.CompareNoCase("endpic")) { ParseLumpName(scanner, mape->endpic); } else if (!pname.CompareNoCase("endcast")) { scanner.MustGetBoolToken(); if (scanner.Number) strcpy(mape->endpic, "$CAST"); else strcpy(mape->endpic, "-"); } else if (!pname.CompareNoCase("endbunny")) { scanner.MustGetBoolToken(); if (scanner.Number) strcpy(mape->endpic, "$BUNNY"); else strcpy(mape->endpic, "-"); } else if (!pname.CompareNoCase("endgame")) { scanner.MustGetBoolToken(); if (scanner.Number) strcpy(mape->endpic, "!"); else strcpy(mape->endpic, "-"); } else if (!pname.CompareNoCase("exitpic")) { ParseLumpName(scanner, mape->exitpic); } else if (!pname.CompareNoCase("enterpic")) { ParseLumpName(scanner, mape->enterpic); } else if (!pname.CompareNoCase("nointermission")) { scanner.MustGetBoolToken(); mape->nointermission = scanner.Number; } else if (!pname.CompareNoCase("partime")) { scanner.MustGetValue(false); mape->partime = TICRATE * scanner.Number; } else if (!pname.CompareNoCase("intertext")) { mape->InterText = ParseMultiString(scanner, 1); if (mape->InterText.IsEmpty()) return 0; } else if (!pname.CompareNoCase("intertextsecret")) { mape->InterTextSecret = ParseMultiString(scanner, 1); if (mape->InterTextSecret.IsEmpty()) return 0; } else if (!pname.CompareNoCase("interbackdrop")) { ParseLumpName(scanner, mape->interbackdrop); } else if (!pname.CompareNoCase("intermusic")) { ParseLumpName(scanner, mape->intermusic); } else if (!pname.CompareNoCase("episode")) { FString Episode = ParseMultiString(scanner, 1); if (Episode.IsEmpty()) return 0; if (Episode.Compare("-") == 0) { // clear the given episode for (unsigned i = 0; i < AllEpisodes.Size(); i++) { if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0) { AllEpisodes.Delete(i); break; } } } else { auto split = Episode.Split("\n"); // add the given episode FEpisode epi; epi.mEpisodeName = strbin1(split[1]); epi.mEpisodeMap = mape->MapName; epi.mPicName = split[0]; epi.mShortcut = split[2][0]; unsigned i; for (i = 0; i < AllEpisodes.Size(); i++) { if (AllEpisodes[i].mEpisodeMap.CompareNoCase(mape->MapName) == 0) { AllEpisodes[i] = std::move(epi); break; } } if (i == AllEpisodes.Size()) { AllEpisodes.Push(epi); } } } else if (!pname.CompareNoCase("bossaction")) { scanner.MustGetToken(TK_Identifier); int classnum, special, tag; if (!stricmp(scanner.String, "clear")) { // mark level free of boss actions classnum = special = tag = -1; mape->BossActions.Clear(); } else { FName type = scanner.String; scanner.MustGetToken(','); scanner.MustGetValue(false); int special = scanner.Number; scanner.MustGetToken(','); scanner.MustGetValue(false); int tag = scanner.Number; // allow no 0-tag specials here, unless a level exit. if (tag != 0 || special == 11 || special == 51 || special == 52 || special == 124) { // This cannot be evaluated here because this needs to be done in the context of the level being used. FSpecialAction & bossact = mape->BossActions[mape->BossActions.Reserve(1)]; bossact = { type, special | 0x40000000, {tag} }; }; } } else { // Skip over all unknown properties. do { if (!scanner.CheckValue(true)) { scanner.MustGetAnyToken(); if (scanner.TokenType != TK_Identifier && scanner.TokenType != TK_StringConst && scanner.TokenType != TK_True && scanner.TokenType != TK_False) { scanner.ScriptError("Identifier or value expected"); } } } while (scanner.CheckToken(',')); } return 1; }
static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClassActor *cls) { PType *type; int maxelems = 1; // Only non-native classes may have user variables. if (!cls->bRuntimeClass) { sc.ScriptError("Native classes may not have user variables"); } // Read the type and make sure it's acceptable. sc.MustGetAnyToken(); if (sc.TokenType != TK_Int && sc.TokenType != TK_Float) { sc.ScriptMessage("User variables must be of type 'int' or 'float'"); FScriptPosition::ErrorCounter++; } type = sc.TokenType == TK_Int ? (PType *)TypeSInt32 : (PType *)TypeFloat64; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee // no clashes with internal member variable names. if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0) { sc.ScriptMessage("User variable names must begin with \"user_\""); FScriptPosition::ErrorCounter++; } FName symname = sc.String; // We must ensure that we do not define duplicates, even when they come from a parent table. if (symt->FindSymbol(symname, true) != NULL) { sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; return; } if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression(sc, cls, true); if (!expr->isConstant()) { sc.ScriptMessage("Array size must be a constant"); FScriptPosition::ErrorCounter++; maxelems = 1; } else { maxelems = static_cast<FxConstant *>(expr)->GetValue().GetInt(); } sc.MustGetToken(']'); if (maxelems <= 0) { sc.ScriptMessage("Array size must be positive"); FScriptPosition::ErrorCounter++; maxelems = 1; } type = NewArray(type, maxelems); } sc.MustGetToken(';'); PField *sym = cls->AddField(symname, type, 0); if (sym == NULL) { sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
static void ParseArgListDef(FScanner &sc, PClassActor *cls, TArray<PType *> &args, TArray<DWORD> &argflags) { if (!sc.CheckToken(')')) { while (sc.TokenType != ')') { int flags = 0; PType *type = NULL; PClass *restrict = NULL; // Retrieve flags before type name for (;;) { if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) { } else { break; } } // Read the variable type sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Bool: type = TypeBool; break; case TK_Int: type = TypeSInt32; break; case TK_Float: type = TypeFloat64; break; case TK_Sound: type = TypeSound; break; case TK_String: type = TypeString; break; case TK_Name: type = TypeName; break; case TK_State: type = TypeState; break; case TK_Color: type = TypeColor; break; case TK_Class: sc.MustGetToken('<'); sc.MustGetToken(TK_Identifier); // Get class name restrict = PClass::FindClass(sc.String); if (restrict == NULL) { sc.ScriptMessage("Unknown class type %s", sc.String); FScriptPosition::ErrorCounter++; } else { type = NewClassPointer(restrict); } sc.MustGetToken('>'); break; case TK_Ellipsis: // Making the final type NULL signals a varargs function. type = NULL; sc.MustGetToken(')'); sc.UnGet(); break; default: sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); type = TypeSInt32; FScriptPosition::ErrorCounter++; break; } // Read the optional variable name if (!sc.CheckToken(',') && !sc.CheckToken(')')) { sc.MustGetToken(TK_Identifier); } else { sc.UnGet(); } if (sc.CheckToken('=')) { flags |= VARF_Optional; FxExpression *def = ParseParameter(sc, cls, type, true); delete def; } args.Push(type); argflags.Push(flags); sc.MustGetAnyToken(); if (sc.TokenType != ',' && sc.TokenType != ')') { sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); } } } sc.MustGetToken(';'); }
static void ParseNativeVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) { FExpressionType valuetype; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("variables can only be imported by internal class and actor definitions!"); FScriptPosition::ErrorCounter++; } // Read the type and make sure it's int or float. sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Int: valuetype = VAL_Int; break; case TK_Float: valuetype = VAL_Float; break; case TK_Angle_t: valuetype = VAL_Angle; break; case TK_Fixed_t: valuetype = VAL_Fixed; break; case TK_Bool: valuetype = VAL_Bool; break; case TK_Identifier: valuetype = VAL_Object; // Todo: Object type sc.ScriptError("Object type variables not implemented yet!"); break; default: sc.ScriptError("Invalid variable type %s", sc.String); return; } sc.MustGetToken(TK_Identifier); FName symname = sc.String; if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression (sc, cls); int maxelems = expr->EvalExpression(NULL).GetInt(); delete expr; sc.MustGetToken(']'); valuetype.MakeArray(maxelems); } sc.MustGetToken(';'); const FVariableInfo *vi = FindVariable(symname, cls); if (vi == NULL) { sc.ScriptError("Unknown native variable '%s'", symname.GetChars()); } PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = vi->address; // todo sym->ValueType = valuetype; sym->bUserVar = false; if (symt->AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
bool FIntermissionActionScroller::ParseKey(FScanner &sc) { struct ScrollType { const char *Name; EScrollDir Type; } const ST[] = { { "Left", SCROLL_Left }, { "Right", SCROLL_Right }, { "Up", SCROLL_Up }, { "Down", SCROLL_Down }, { NULL, SCROLL_Left } }; if (sc.Compare("ScrollDirection")) { sc.MustGetToken('='); sc.MustGetToken(TK_Identifier); int v = sc.MatchString(&ST[0].Name, sizeof(ST[0])); if (v != -1) mScrollDir = ST[v].Type; return true; } else if (sc.Compare("InitialDelay")) { sc.MustGetToken('='); if (!sc.CheckToken('-')) { sc.MustGetFloat(); mScrollDelay = int(sc.Float*TICRATE); } else { sc.MustGetToken(TK_IntConst); mScrollDelay = sc.Number; } return true; } else if (sc.Compare("ScrollTime")) { sc.MustGetToken('='); if (!sc.CheckToken('-')) { sc.MustGetFloat(); mScrollTime = int(sc.Float*TICRATE); } else { sc.MustGetToken(TK_IntConst); mScrollTime = sc.Number; } return true; } else if (sc.Compare("Background2")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mSecondPic = sc.String; return true; } else return Super::ParseKey(sc); }
static void ParseActionDef (FScanner &sc, PClass *cls) { enum { OPTIONAL = 1 }; unsigned int error = 0; const AFuncDesc *afd; FName funcname; FString args; TArray<FxExpression *> DefaultParams; bool hasdefaults = false; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { sc.ScriptMessage ("Action functions can only be imported by internal class and actor definitions!"); ++error; } sc.MustGetToken(TK_Native); sc.MustGetToken(TK_Identifier); funcname = sc.String; afd = FindFunction(sc.String); if (afd == NULL) { sc.ScriptMessage ("The function '%s' has not been exported from the executable.", sc.String); ++error; } sc.MustGetToken('('); if (!sc.CheckToken(')')) { while (sc.TokenType != ')') { int flags = 0; char type = '@'; // Retrieve flags before type name for (;;) { if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native)) { } else { break; } } // Read the variable type sc.MustGetAnyToken(); switch (sc.TokenType) { case TK_Bool: case TK_Int: type = 'x'; break; case TK_Float: type = 'y'; break; case TK_Sound: type = 's'; break; case TK_String: type = 't'; break; case TK_Name: type = 't'; break; case TK_State: type = 'l'; break; case TK_Color: type = 'c'; break; case TK_Class: sc.MustGetToken('<'); sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care sc.MustGetToken('>'); type = 'm'; break; case TK_Ellipsis: type = '+'; sc.MustGetToken(')'); sc.UnGet(); break; default: sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars()); type = 'x'; FScriptPosition::ErrorCounter++; break; } // Read the optional variable name if (!sc.CheckToken(',') && !sc.CheckToken(')')) { sc.MustGetToken(TK_Identifier); } else { sc.UnGet(); } FxExpression *def; if (sc.CheckToken('=')) { hasdefaults = true; flags |= OPTIONAL; def = ParseParameter(sc, cls, type, true); } else { def = NULL; } DefaultParams.Push(def); if (!(flags & OPTIONAL) && type != '+') { type -= 'a' - 'A'; } args += type; sc.MustGetAnyToken(); if (sc.TokenType != ',' && sc.TokenType != ')') { sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars()); } } } sc.MustGetToken(';'); if (afd != NULL) { PSymbolActionFunction *sym = new PSymbolActionFunction(funcname); sym->Arguments = args; sym->Function = afd->Function; if (hasdefaults) { sym->defaultparameterindex = StateParams.Size(); for(unsigned int i = 0; i < DefaultParams.Size(); i++) { StateParams.Add(DefaultParams[i], cls, true); } } else { sym->defaultparameterindex = -1; } if (error) { FScriptPosition::ErrorCounter += error; } else if (cls->Symbols.AddSymbol (sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in class '%s'.", funcname.GetChars(), cls->TypeName.GetChars()); FScriptPosition::ErrorCounter++; } } }
static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) { FExpressionType valuetype; // Only non-native classes may have user variables. if (!cls->bRuntimeClass) { sc.ScriptError("Native classes may not have user variables"); } // Read the type and make sure it's int. sc.MustGetAnyToken(); if (sc.TokenType != TK_Int) { sc.ScriptMessage("User variables must be of type int"); FScriptPosition::ErrorCounter++; } valuetype = VAL_Int; sc.MustGetToken(TK_Identifier); // For now, restrict user variables to those that begin with "user_" to guarantee // no clashes with internal member variable names. if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0) { sc.ScriptMessage("User variable names must begin with \"user_\""); FScriptPosition::ErrorCounter++; } FName symname = sc.String; if (sc.CheckToken('[')) { FxExpression *expr = ParseExpression(sc, cls); int maxelems = expr->EvalExpression(NULL).GetInt(); delete expr; sc.MustGetToken(']'); if (maxelems <= 0) { sc.ScriptMessage("Array size must be positive"); FScriptPosition::ErrorCounter++; maxelems = 1; } valuetype.MakeArray(maxelems); } sc.MustGetToken(';'); // We must ensure that we do not define duplicates, even when they come from a parent table. if (symt->FindSymbol(symname, true) != NULL) { sc.ScriptMessage ("'%s' is already defined in '%s' or one of its ancestors.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; return; } PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1)); sym->ValueType = valuetype; sym->bUserVar = true; if (symt->AddSymbol(sym) == NULL) { delete sym; sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); FScriptPosition::ErrorCounter++; } }
bool FIntermissionAction::ParseKey(FScanner &sc) { if (sc.Compare("music")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mMusic = sc.String; mMusicOrder = 0; if (sc.CheckToken(',')) { sc.MustGetToken(TK_IntConst); mMusicOrder = sc.Number; } return true; } else if (sc.Compare("cdmusic")) { sc.MustGetToken('='); sc.MustGetToken(TK_IntConst); mCdTrack = sc.Number; mCdId = 0; if (sc.CheckToken(',')) { sc.MustGetToken(TK_IntConst); mCdId = sc.Number; } return true; } else if (sc.Compare("Time")) { sc.MustGetToken('='); if (!sc.CheckToken('-')) { sc.MustGetFloat(); mDuration = int(sc.Float*TICRATE); } else { sc.MustGetToken(TK_IntConst); mDuration = sc.Number; } return true; } else if (sc.Compare("Background")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mBackground = sc.String; mFlatfill = 0; if (sc.CheckToken(',')) { sc.MustGetToken(TK_IntConst); mFlatfill = !!sc.Number; if (sc.CheckToken(',')) { sc.MustGetToken(TK_StringConst); mPalette = sc.String; } } return true; } else if (sc.Compare("Sound")) { sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); mSound = sc.String; return true; } else if (sc.Compare("Draw")) { FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); pat->mName = sc.String; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); pat->x = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); pat->y = sc.Number; pat->mCondition = NAME_None; return true; } else if (sc.Compare("DrawConditional")) { FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; sc.MustGetToken('='); sc.MustGetToken(TK_StringConst); pat->mCondition = sc.String; sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); pat->mName = sc.String; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); pat->x = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); pat->y = sc.Number; return true; } else return false; }
FFont::FFont (const char *name, const char *nametemplate, const char *filetemplate, int lfirst, int lcount, int start, int fdlump, int spacewidth, bool notranslate, bool iwadonly) { int i; FTextureID lump; char buffer[12]; int maxyoffs; bool doomtemplate = (nametemplate && (gameinfo.gametype & GAME_DoomChex)) ? strncmp (nametemplate, "STCFN", 5) == 0 : false; DVector2 Scale = { 1, 1 }; noTranslate = notranslate; Lump = fdlump; GlobalKerning = false; FontName = name; Next = FirstFont; FirstFont = this; Cursor = '_'; ActiveColors = 0; SpaceWidth = 0; FontHeight = 0; uint8_t pp = 0; for (auto &p : PatchRemap) p = pp++; translateUntranslated = false; int FixedWidth = 0; maxyoffs = 0; TMap<int, FTexture*> charMap; int minchar = INT_MAX; int maxchar = INT_MIN; // Read the font's configuration. // This will not be done for the default fonts, because they are not atomic and the default content does not need it. TArray<FolderEntry> folderdata; if (filetemplate != nullptr) { FStringf path("fonts/%s/", filetemplate); // If a name template is given, collect data from all resource files. // For anything else, each folder is being treated as an atomic, self-contained unit and mixing from different glyph sets is blocked. Wads.GetLumpsInFolder(path, folderdata, nametemplate == nullptr); //if (nametemplate == nullptr) { FStringf infpath("fonts/%s/font.inf", filetemplate); unsigned index = folderdata.FindEx([=](const FolderEntry &entry) { return infpath.CompareNoCase(entry.name) == 0; }); if (index < folderdata.Size()) { FScanner sc; sc.OpenLumpNum(folderdata[index].lumpnum); while (sc.GetToken()) { sc.TokenMustBe(TK_Identifier); if (sc.Compare("Kerning")) { sc.MustGetValue(false); GlobalKerning = sc.Number; } else if (sc.Compare("Scale")) { sc.MustGetValue(true); Scale.Y = Scale.X = sc.Float; if (sc.CheckToken(',')) { sc.MustGetValue(true); Scale.Y = sc.Float; } } else if (sc.Compare("SpaceWidth")) { sc.MustGetValue(false); SpaceWidth = sc.Number; } else if (sc.Compare("FontHeight")) { sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("CellSize")) { sc.MustGetValue(false); FixedWidth = sc.Number; sc.MustGetToken(','); sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("Translationtype")) { sc.MustGetToken(TK_Identifier); if (sc.Compare("console")) { TranslationType = 1; } else if (sc.Compare("standard")) { TranslationType = 0; } else { sc.ScriptError("Unknown translation type %s", sc.String); } } } } } } if (FixedWidth > 0) { ReadSheetFont(folderdata, FixedWidth, FontHeight, Scale); Type = Folder; } else { if (nametemplate != nullptr) { if (!iwadonly) { for (i = 0; i < lcount; i++) { int position = lfirst + i; mysnprintf(buffer, countof(buffer), nametemplate, i + start); lump = TexMan.CheckForTexture(buffer, ETextureType::MiscPatch); if (doomtemplate && lump.isValid() && i + start == 121) { // HACKHACK: Don't load STCFN121 in doom(2), because // it's not really a lower-case 'y' but a '|'. // Because a lot of wads with their own font seem to foolishly // copy STCFN121 and make it a '|' themselves, wads must // provide STCFN120 (x) and STCFN122 (z) for STCFN121 to load as a 'y'. if (!TexMan.CheckForTexture("STCFN120", ETextureType::MiscPatch).isValid() || !TexMan.CheckForTexture("STCFN122", ETextureType::MiscPatch).isValid()) { // insert the incorrectly named '|' graphic in its correct position. position = 124; } } if (lump.isValid()) { Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, TexMan.GetTexture(lump)); } } } else { FTexture *texs[256] = {}; if (lcount > 256 - start) lcount = 256 - start; for (i = 0; i < lcount; i++) { TArray<FTextureID> array; mysnprintf(buffer, countof(buffer), nametemplate, i + start); TexMan.ListTextures(buffer, array, true); for (auto entry : array) { FTexture *tex = TexMan.GetTexture(entry, false); if (tex && tex->SourceLump >= 0 && Wads.GetLumpFile(tex->SourceLump) <= Wads.GetIwadNum() && tex->UseType == ETextureType::MiscPatch) { texs[i] = tex; } } } if (doomtemplate) { // Handle the misplaced '|'. if (texs[121 - '!'] && !texs[120 - '!'] && !texs[122 - '!'] && !texs[124 - '!']) { texs[124 - '!'] = texs[121 - '!']; texs[121 - '!'] = nullptr; } } for (i = 0; i < lcount; i++) { if (texs[i]) { int position = lfirst + i; Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, texs[i]); } } } } if (folderdata.Size() > 0) { // all valid lumps must be named with a hex number that represents its Unicode character index. for (auto &entry : folderdata) { char *endp; auto base = ExtractFileBase(entry.name); auto position = strtoll(base.GetChars(), &endp, 16); if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff))) { auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch); if (lump.isValid()) { if ((int)position < minchar) minchar = (int)position; if ((int)position > maxchar) maxchar = (int)position; auto tex = TexMan.GetTexture(lump); tex->SetScale(Scale); charMap.Insert((int)position, tex); Type = Folder; } } } } FirstChar = minchar; LastChar = maxchar; auto count = maxchar - minchar + 1; Chars.Resize(count); int fontheight = 0; for (i = 0; i < count; i++) { auto lump = charMap.CheckKey(FirstChar + i); if (lump != nullptr) { FTexture *pic = *lump; if (pic != nullptr) { int height = pic->GetDisplayHeight(); int yoffs = pic->GetDisplayTopOffset(); if (yoffs > maxyoffs) { maxyoffs = yoffs; } height += abs(yoffs); if (height > fontheight) { fontheight = height; } } pic->SetUseType(ETextureType::FontChar); if (!noTranslate) { Chars[i].OriginalPic = pic; Chars[i].TranslatedPic = new FImageTexture(new FFontChar1(pic->GetImage()), ""); Chars[i].TranslatedPic->CopySize(pic); Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar); TexMan.AddTexture(Chars[i].TranslatedPic); } else { Chars[i].TranslatedPic = pic; } Chars[i].XMove = Chars[i].TranslatedPic->GetDisplayWidth(); } else { Chars[i].TranslatedPic = nullptr; Chars[i].XMove = INT_MIN; } } if (SpaceWidth == 0) // An explicit override from the .inf file must always take precedence { if (spacewidth != -1) { SpaceWidth = spacewidth; } else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr) { SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2; } else { SpaceWidth = 4; } } if (FontHeight == 0) FontHeight = fontheight; FixXMoves(); } if (!noTranslate) LoadTranslations(); }