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);
        }
Example #3
0
        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);
        }