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); }
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); }
public IActionResult SearchGames(string searchPattern) { //Matching all letters to lowercase (the same thing is happening to game title during comparison) searchPattern = searchPattern.ToLower(); var games = _games.GetAll(); if (!string.IsNullOrEmpty(searchPattern)) { games = games.Where(game => game.Title.ToLower().Contains(searchPattern)); } var listingResults = games .Select(result => new Game { Id = result.Id, ImageUrl = result.ImageUrl, Title = result.Title, Genre = result.Genre, ReleaseYear = result.ReleaseYear, Rating = result.Rating, Platform = result.Platform }); var modelGames = listingResults.ToList(); var model = new GameIndex { Games = modelGames }; return(View(model)); }
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); }
internal static string GetLocalizedGamePlayOriginalText(GameIndex gameIndex) { return (gameIndex == GameIndex.Thief1 ? Misc.LText.PlayOriginalGameMenu.Thief1_PlayOriginal: gameIndex == GameIndex.Thief2 ? Misc.LText.PlayOriginalGameMenu.Thief2_PlayOriginal: gameIndex == GameIndex.Thief3 ? Misc.LText.PlayOriginalGameMenu.Thief3_PlayOriginal: Misc.LText.PlayOriginalGameMenu.SystemShock2_PlayOriginal); }
internal static string GetLocalizedGameNameColon(GameIndex gameIndex) { return (gameIndex == GameIndex.Thief1 ? Misc.LText.Global.Thief1_Colon: gameIndex == GameIndex.Thief2 ? Misc.LText.Global.Thief2_Colon: gameIndex == GameIndex.Thief3 ? Misc.LText.Global.Thief3_Colon: Misc.LText.Global.SystemShock2_Colon); }
internal static string GetShortLocalizedGameName(GameIndex gameIndex) { return (gameIndex == GameIndex.Thief1 ? Misc.LText.Global.Thief1_Short: gameIndex == GameIndex.Thief2 ? Misc.LText.Global.Thief2_Short: gameIndex == GameIndex.Thief3 ? Misc.LText.Global.Thief3_Short: Misc.LText.Global.SystemShock2_Short); }
//public IActionResult Index() //{ // var gameModels = _games.GetAll(); // var listingResults = gameModels // .Select(result => new Game // { // Id = result.Id, // ImageUrl = result.ImageUrl, // Title = result.Title, // Genre = result.Genre, // ReleaseYear = result.ReleaseYear, // Rating = result.Rating, // Platform = result.Platform // }); // var model = new GameIndex() // { // Games = listingResults // }; // return View(model); //} public IActionResult Index(string sortOption) { var gameModels = _games.GetAll(); var listingResults = gameModels .Select(result => new Game { Id = result.Id, ImageUrl = result.ImageUrl, Title = result.Title, Genre = result.Genre, ReleaseYear = result.ReleaseYear, Rating = result.Rating, Platform = result.Platform }); var modelGames = listingResults.ToList(); var model = new GameIndex { Games = modelGames }; switch (sortOption) { case "Title": model.Games = modelGames.OrderBy(game => game.Title); break; case "Genre": model.Games = modelGames.OrderBy(game => game.Genre); break; case "Platform": model.Games = modelGames.OrderBy(game => game.Rating); break; case "ReleaseYear": model.Games = modelGames.OrderByDescending(game => game.ReleaseYear); break; case "Rating": model.Games = modelGames.OrderByDescending(game => game.Rating); break; default: model.Games = modelGames; break; } return(View(model)); }
/// <summary> /// Remove our footprints from any config files we may have temporarily stomped on. /// If it fails, oh well. It's no worse than before, we just end up with ourselves as the loader, /// and the user will get a message about that if they start the game later. /// </summary> /// <param name="perGameGoFlags"> /// An array of bools, of length <see cref="SupportedGameCount"/>. Each bool says whether to go ahead and /// do the reset work for that game. If false, we skip it. /// </param> // @CAN_RUN_BEFORE_VIEW_INIT internal static void ResetGameConfigTempChanges(bool[]?perGameGoFlags = null) { AssertR(perGameGoFlags == null || perGameGoFlags.Length == SupportedGameCount, nameof(perGameGoFlags) + " length does not match " + nameof(SupportedGameCount)); for (int i = 0; i < SupportedGameCount; i++) { GameIndex gameIndex = (GameIndex)i; string gameExe = Config.GetGameExe(gameIndex); try { if ((perGameGoFlags == null || perGameGoFlags[i]) && // Only try to un-stomp the configs for the game if the game was actually specified !gameExe.IsWhiteSpace()) { // For Dark, we need to know if the game exe itself actually exists. if (GameIsDark(gameIndex) && File.Exists(gameExe)) { string gamePath = Config.GetGamePath(gameIndex); SetCamCfgLanguage(gamePath, ""); SetDarkFMSelector(gameIndex, gamePath, resetSelector: true); if (gameIndex is GameIndex.Thief1 or GameIndex.Thief2) { FixCharacterDetailLine(gamePath); } } else { // For Thief 3, we actually just want to know if SneakyOptions.ini exists. The game // itself existing is not technically a requirement. string soIni = Paths.GetSneakyOptionsIni(); if (!soIni.IsEmpty() && File.Exists(soIni)) { SetT3FMSelector(resetSelector: true); } } } } catch (Exception ex) { Log("Exception trying to unset temp config values\r\n" + "GameIndex: " + gameIndex + "\r\n" + "GameExe: " + gameExe, ex); Dialogs.ShowError("Error attempting to restore previous game config file settings.\r\n" + "Game: " + gameIndex + "\r\n" + "Game exe: " + gameExe); } } }
CheckAndReturnFinalGameExeAndGamePath(GameIndex gameIndex, bool playingOriginalGame, bool playMP = false) { var failed = (Success : false, GameExe : "", GamePath : ""); string gameName = GetLocalizedGameName(gameIndex); string gameExe = Config.GetGameExe(gameIndex); #region Fail if game path is blank string gamePath = Config.GetGamePath(gameIndex); if (gamePath.IsEmpty()) { Log("Game path is empty for " + gameIndex, stackTrace: true); Dialogs.ShowError(gameName + ":\r\n" + ErrorText.GamePathEmpty); return(failed); } #endregion if (playMP) { gameExe = Path.Combine(gamePath, Paths.T2MPExe); } #region Exe: Fail if blank or not found if (gameExe.IsEmpty() || !File.Exists(gameExe)) { string exeNotFoundMessage = playingOriginalGame ? LText.AlertMessages.Play_ExecutableNotFound : LText.AlertMessages.Play_ExecutableNotFoundFM; Dialogs.ShowError(gameName + ":\r\n" + exeNotFoundMessage); return(failed); } #endregion #region Exe: Fail if already running if (GameIsRunning(gameExe, checkAllGames: true)) { Dialogs.ShowAlert(LText.AlertMessages.Play_AnyGameIsRunning, LText.AlertMessages.Alert); return(failed); } #endregion return(true, gameExe, gamePath); }
/* * I'm not completely comfortable putting these in here... They're sort of in a no-man's land between * belonging here or belonging outside. * Arguments for being in here: * -They depend on the game paths which are in here * -They conceptually go with the other Get*Exe() methods * Arguments for being outside: * -The actual filenames at the end of their paths are constants from the static paths class * -They access the file system, which feels a bit weird for a config object to do. I feel like the methods * in here should all be "instant" and operate on memory only and not do weird things you don't expect. * (that's the main reason for my discomfort really) * * I'm just going to append "_FromDisk" to their names, document them, and call it serviceable. */ /// <summary> /// Returns the full path of the editor for <paramref name="game"/> if and only if it exists on disk. /// Otherwise, returns the empty string. It will also return the empty string if <paramref name="game"/> /// is not Dark. /// </summary> /// <param name="game"></param> /// <returns></returns> internal string GetEditorExe_FromDisk(GameIndex game) { string gamePath; if (!GameIsDark(game) || (gamePath = GetGamePath(game)).IsEmpty()) { return(""); } string exe = game == GameIndex.SS2 ? Paths.ShockEdExe : Paths.DromEdExe; return(TryCombineFilePathAndCheckExistence(gamePath, exe, out string fullPathExe) ? fullPathExe : ""); }
private void AutodetectGameIni(GameIndex game, TextBox textBox) { string iniFile = ImportType == ImportType.NewDarkLoader ? Paths.NewDarkLoaderIni : Paths.FMSelIni; string fmsPath = Config.GetFMInstallPath(game); if (fmsPath.IsWhiteSpace()) { textBox.Text = ""; } else { textBox.Text = TryCombineFilePathAndCheckExistence(fmsPath, iniFile, out string iniFileFull) ? iniFileFull : ""; } }
public void SetGameIdx(int idx) { if (idx < 0 || idx > 3) { Debug.LogError("idx范围超过 SetGameIdx " + idx); idx = 0; } if ((int)mGameIdx != idx) { mGameIdx = (GameIndex)idx; ResetData(); } else { mGameIdx = (GameIndex)idx; } // mTitle.text = LocalizationManager.Instance.GetValue(titleList[idx]); mTitle.SetKey(titleList[idx]); ViewManager.ReplaceView(objList[idx], gameObject.name); }
internal static string GetEditorExe(GameIndex game) { if (!GameIsDark(game)) { return(""); } string gamePath = Config.GetGamePath(game); if (gamePath.IsEmpty()) { return(""); } string exe = game == SS2 ? Paths.ShockEdExe : Paths.DromEdExe; return(TryCombineFilePathAndCheckExistence(gamePath, exe, out string fullPathExe) ? fullPathExe : ""); }
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" + ""); } }
GetSteamValues(GameIndex game, bool playMP) { // Multiplayer means starting Thief2MP.exe, so we can't really run it through Steam because Steam // will start Thief2.exe if (!playMP && !GetGameSteamId(game).IsEmpty() && Config.GetUseSteamSwitch(game) && Config.LaunchGamesWithSteam && !Config.SteamExe.IsEmpty() && File.Exists(Config.SteamExe)) { string?workingPath = Path.GetDirectoryName(Config.SteamExe); if (workingPath.IsEmpty()) { return(false, "", "", ""); } string args = "-applaunch " + GetGameSteamId(game); return(true, Config.SteamExe, workingPath, args); } else { return(false, "", "", ""); } }
internal string GetFMInstallPath(GameIndex index) => FMInstallPaths[(uint)index];
internal void SetGamePath(GameIndex index, string value) => GamePaths[(uint)index] = value;
internal string GetGamePath(GameIndex index) => GamePaths[(uint)index];
internal void SetGameExe(GameIndex index, string value) => GameExes[(uint)index] = value;
internal string GetGameExe(GameIndex index) => GameExes[(uint)index];
internal void SetStartupAlwaysStartSelector(GameIndex index, bool value) => StartupAlwaysStartSelector[(uint)index] = value;
internal bool GetStartupAlwaysStartSelector(GameIndex index) => StartupAlwaysStartSelector[(uint)index];
internal void SetStartupFMSelectorLines(GameIndex index, List <string> value) => StartupFMSelectorLines[(uint)index] = value;
internal List <string> GetStartupFMSelectorLines(GameIndex index) => StartupFMSelectorLines[(uint)index];
internal void SetUseSteamSwitch(GameIndex index, bool value) => UseSteamSwitches[(uint)index] = value;
internal bool GetUseSteamSwitch(GameIndex index) => UseSteamSwitches[(uint)index];
internal void SetGameEditorDetected(GameIndex index, bool value) => GameEditorDetected[(uint)index] = value;
internal static Error TryGetGameVersion(GameIndex game, out string version) { version = ""; string gameExe = GameIsDark(game) ? Config.GetGameExe(game) : Path.Combine(Config.GetGamePath(Thief3), "Sneaky.dll"); if (gameExe.IsWhiteSpace()) { return(Error.GameExeNotSpecified); } if (!File.Exists(gameExe)) { return(Error.GameExeNotFound); } FileStream? fs = null; BinaryReader?br = null; try { fs = new FileStream(gameExe, FileMode.Open, FileAccess.Read); br = new BinaryReader(fs, Encoding.ASCII, leaveOpen: false); long streamLen = br.BaseStream.Length; if (streamLen > int.MaxValue) { return(Error.ExeIsLargerThanInt); } // Search starting at 88% through the file: 91% (average location) plus some wiggle room (fastest) long pos = GetValueFromPercent(88.0d, streamLen); long byteCount = streamLen - pos; br.BaseStream.Position = pos; byte[] bytes = new byte[byteCount]; br.Read(bytes, 0, (int)byteCount); int verIndex = bytes.IndexOfByteSequence(ProductVersionBytes); // Fallback: search the whole file - still fast, but not as fast if (verIndex == -1) { br.BaseStream.Position = 0; bytes = new byte[streamLen]; br.Read(bytes, 0, (int)streamLen); verIndex = bytes.IndexOfByteSequence(ProductVersionBytes); if (verIndex == -1) { return(Error.GameVersionNotFound); } } // Init with non-null values so we don't start out with two nulls and early-out before we do anything byte[] null2 = { 255, 255 }; for (int i = verIndex + ProductVersionBytes.Length; i < bytes.Length; i++) { if (null2[0] == '\0' && null2[1] == '\0') { break; } null2[0] = null2[1]; null2[1] = bytes[i]; if (bytes[i] > 0) { version += ((char)bytes[i]).ToString(); } } } catch (Exception ex) { Log("Exception reading/searching game exe for version string", ex); version = ""; return(Error.GameExeReadFailed); } finally { br?.Dispose(); fs?.Dispose(); } return(Error.None); }
internal void SetFMInstallPath(GameIndex index, string value) => FMInstallPaths[(uint)index] = value;