internal static bool OpenFMInEditor(FanMission fm) { #region Checks (specific to DromEd) // This should never happen because our menu item is supposed to be hidden for Thief 3 FMs. if (!GameIsDark(fm.Game)) { Log("FM game type is not a Dark Engine game.\r\n" + "FM: " + (!fm.Archive.IsEmpty() ? fm.Archive : fm.InstalledDir) + "\r\n" + "fm.Game was: " + fm.Game, stackTrace: true); Dialogs.ShowError(ErrorText.FMGameTypeIsNotDark); return(false); } GameIndex gameIndex = GameToGameIndex(fm.Game); string gamePath = Config.GetGamePath(gameIndex); if (gamePath.IsEmpty()) { Log("Game path is empty for " + gameIndex, stackTrace: true); Dialogs.ShowError(gameIndex + ":\r\n" + ErrorText.GamePathEmpty); return(false); } string editorExe = Config.GetEditorExe_FromDisk(gameIndex); if (editorExe.IsEmpty()) { Log("Editor executable not found.\r\n" + "FM:" + (!fm.Archive.IsEmpty() ? fm.Archive : fm.InstalledDir) + "\r\n" + "Editor executable: " + editorExe); Dialogs.ShowError(fm.Game == Game.SS2 ? LText.AlertMessages.ShockEd_ExecutableNotFound : LText.AlertMessages.DromEd_ExecutableNotFound); return(false); } #endregion // Just in case, and for consistency Paths.CreateOrClearTempPath(Paths.StubCommTemp); if (gameIndex is GameIndex.Thief1 or GameIndex.Thief2) { GameConfigFiles.FixCharacterDetailLine(gamePath); } // We don't need to do this here, right? SetUsAsSelector(gameIndex, gamePath, PlaySource.Editor); // Since we don't use the stub currently, set this here // NOTE: DromEd game mode doesn't even work for me anymore. Black screen no matter what. So I can't test if we need languages. GameConfigFiles.SetCamCfgLanguage(gamePath, ""); // Why not GenerateMissFlagFileIfRequired(fm); // We don't need the stub for DromEd, cause we don't need to pass anything except the fm folder StartExe(editorExe, gamePath, "-fm=\"" + fm.InstalledDir + "\""); return(true); }
internal static bool PlayOriginalGame(GameIndex game, bool playMP = false) { (bool success, string gameExe, string gamePath) = CheckAndReturnFinalGameExeAndGamePath(game, playingOriginalGame: true, playMP); if (!success) { return(false); } Paths.CreateOrClearTempPath(Paths.StubCommTemp); if (game is GameIndex.Thief1 or GameIndex.Thief2) { GameConfigFiles.FixCharacterDetailLine(gamePath); } SetUsAsSelector(game, gamePath, PlaySource.OriginalGame); #if !ReleaseBeta && !ReleasePublic string args = Config.ForceWindowed ? "+force_windowed" : ""; #else string args = ""; #endif string workingPath = Config.GetGamePath(game); var sv = GetSteamValues(game, playMP); if (sv.Success) { (_, gameExe, workingPath, args) = sv; } WriteStubCommFile(null, gamePath, originalT3: game == GameIndex.Thief3); StartExe(gameExe, workingPath, args); return(true); }
GetDarkFMLanguage(GameIndex game, string fmArchive, string fmInstalledDir) { string sLanguage; bool bForceLanguage; var(_, fmLanguage, _, _, _) = GameConfigFiles.GetInfoFromCamModIni(Config.GetGamePath(game), out _, langOnly: true); // bForceLanguage gets set to something specific in every possible case, effectively meaning the // fm_language_forced value is always ignored. Weird, but FMSel's code does exactly this, so meh? // NOTE: Although I'm using FMSel from ND 1.26 as a reference, ND 1.27's is exactly the same. string fmInstPath = Path.Combine(Config.GetFMInstallPath(game), fmInstalledDir); if (!fmLanguage.IsEmpty()) { var fmSupportedLangs = GetFMSupportedLanguages(fmArchive, fmInstPath, earlyOutOnEnglish: false); if (fmSupportedLangs.ContainsI(fmLanguage)) { // FMSel doesn't set this because it's already getting it from the game meaning it's set // already, but we have to set it ourselves because we're getting it manually sLanguage = fmLanguage; bForceLanguage = true; } else { // language not supported, use fallback sLanguage = fmSupportedLangs.Count > 0 ? fmSupportedLangs[0] : ""; bForceLanguage = false; } } else { /* * So, if I'm reading the API notes right, it looks like NewDark actually picks the right language * automatically if it can find it in DARKINST.CFG. We set sLanguage to either: * -force that language to be used if it's available (otherwise we use the below crappily-guessed * fallback), or * -give it a crappily-guessed fallback value so that if NewDark can't find a language, we at least * have SOMETHING to give it so text won't be blank, even though it's likely it'll be the wrong * language if it isn't English. */ // FMSel's comment: // determine FM default language (used if the FM doesn't support the language set in dark by the "language" cfg var) // FMSel's comment: // determine if FM has languages defined, if it does and english is among them, then english is the fallback // if english is not among them then pick another, if no languages are found then no fallback language will // be defined var langs = GetFMSupportedLanguages(fmArchive, fmInstPath, earlyOutOnEnglish: true); // Use first available language (which will be English if English is available) sLanguage = langs.Count == 0 ? "" : langs[0]; bForceLanguage = false; } return(sLanguage, bForceLanguage); }
private static void SetUsAsSelector(GameIndex game, string gamePath, PlaySource playSource) { bool success = GameIsDark(game) ? GameConfigFiles.SetDarkFMSelector(game, gamePath) : GameConfigFiles.SetT3FMSelector(); if (!success) { Log("Unable to set us as the selector for " + Config.GetGameExe(game) + " (" + (GameIsDark(game) ? nameof(GameConfigFiles.SetDarkFMSelector) : nameof(GameConfigFiles.SetT3FMSelector)) + " returned false)\r\n" + "Source: " + playSource, stackTrace: true); Dialogs.ShowError( "Failed to set AngelLoader as the FM selector.\r\n\r\n" + "Game: " + game + "\r\n" + "Game exe: " + Config.GetGameExe(game) + "\r\n" + "Source: " + playSource + "\r\n" + ""); } }
private static void WriteStubCommFile(FanMission?fm, string gamePath, bool originalT3 = false) { string sLanguage = ""; bool? bForceLanguage = null; if (fm == null) { if (!originalT3) { GameConfigFiles.SetCamCfgLanguage(gamePath, ""); } } else if (GameIsDark(fm.Game)) { bool langIsDefault = fm.SelectedLang.EqualsI(FMLanguages.DefaultLangKey); if (langIsDefault) { // For Dark, we have to do this semi-manual stuff. (sLanguage, bForceLanguage) = FMLanguages.GetDarkFMLanguage(GameToGameIndex(fm.Game), fm.Archive, fm.InstalledDir); } else { sLanguage = fm.SelectedLang; bForceLanguage = true; } GameConfigFiles.SetCamCfgLanguage(gamePath, langIsDefault ? "" : fm.SelectedLang); } // For Thief 3, Sneaky Upgrade does the entire language thing for me, Builder bless snobel once again. // I just can't tell you how much I appreciate how much work SU does for me, even right down to handling // the "All The World's a Stage" custom sound extract thing. // So, I don't have to do anything whatsoever here, just pass blank and carry on. Awesome! try { // IMPORTANT (Stub comm file encoding): // Encoding MUST be "new UTF8Encoding(false, true)" or the C++ stub won't read it (it doesn't // handle the byte order mark). using var sw = new StreamWriter(Paths.StubCommFilePath, append: false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); sw.WriteLine("PlayOriginalGame=" + (fm == null)); if (fm != null) { sw.WriteLine("SelectedFMName=" + fm.InstalledDir); sw.WriteLine("DisabledMods=" + fm.DisabledMods); // Pass blank if we have nothing, so the stub will leave whatever was in there before if (!sLanguage.IsEmpty()) { sw.WriteLine("Language=" + sLanguage); } if (bForceLanguage != null) { sw.WriteLine("ForceLanguage=" + (bool)bForceLanguage); } } } catch (Exception ex) { Log("Exception writing stub file '" + Paths.StubFileName + "'\r\n" + (fm == null ? "Original game" : "FM"), ex); Dialogs.ShowError("Unable to write stub comm file. " + (fm == null ? "Game may not start correctly." : "FM cannot be passed to the game and therefore cannot be played.")); } }
private static bool PlayFM(FanMission fm, bool playMP = false) { if (!GameIsKnownAndSupported(fm.Game)) { Log("Game is unknown or unsupported for FM " + (!fm.Archive.IsEmpty() ? fm.Archive : fm.InstalledDir) + "\r\n" + "fm.Game was: " + fm.Game, stackTrace: true); Dialogs.ShowError(ErrorText.FMGameTypeUnknownOrUnsupported); return(false); } GameIndex gameIndex = GameToGameIndex(fm.Game); (bool success, string gameExe, string gamePath) = CheckAndReturnFinalGameExeAndGamePath(gameIndex, playingOriginalGame: false, playMP); if (!success) { return(false); } // Always do this for robustness, see below Paths.CreateOrClearTempPath(Paths.StubCommTemp); if (gameIndex is GameIndex.Thief1 or GameIndex.Thief2) { GameConfigFiles.FixCharacterDetailLine(gamePath); } SetUsAsSelector(gameIndex, gamePath, PlaySource.FM); string steamArgs = ""; string workingPath = Config.GetGamePath(gameIndex); var sv = GetSteamValues(gameIndex, playMP); if (sv.Success) { (_, gameExe, workingPath, steamArgs) = sv; } // 2019-10-31: Always use the stub now, in prep for matching FMSel's language stuff // BUG: Possible stub comm file not being deleted in the following scenario: // You launch a game through Steam, but the game doesn't actually launch (because you don't have // it in your Steam library or any other situation in which it gets cancelled). Because the game // never runs, it never deletes the stub comm file. The next time the game runs, it finds the stub // file and loads up whatever FM was specified. This won't happen if you launch an FM or original // game from AngelLoader, as we delete or overwrite the stub file ourselves before playing anything, // but if you were to run the game manually, it would load whatever FM was specified in the stub // once, and then delete it, so if you ran it again it would properly start the original game and // everything would be fine again. // I could solve it if there was a way to detect if we were being launched through Steam. I don't // know if there is, but then I could just specify a Steam=True line in the stub file, and then // if we're being launched through steam we read and act on it as usual, but if we're not, then // we just delete it and ignore. // I'll have to buy the games on Steam to test this. Or just buy one so I can have one game that // works and one that doesn't, so I can test both paths. #if !ReleaseBeta && !ReleasePublic string args = !steamArgs.IsEmpty() ? steamArgs : Config.ForceWindowed ? "+force_windowed -fm" : "-fm"; #else string args = !steamArgs.IsEmpty() ? steamArgs : "-fm"; #endif GenerateMissFlagFileIfRequired(fm); WriteStubCommFile(fm, gamePath); StartExe(gameExe, workingPath, args); // Don't clear the temp folder here, because the stub program will need to read from it. It will // delete the temp file itself after it's done with it. return(true); }