tools/installer: create TR2 installer
This creates a TR2 installer in similar fashion to TR1. The installers themselves are basically directory tree copiers, rather than having to extract from BIN/ISO like TR1.
4
tools/installer/TR2X_Installer/App.xaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<Application
|
||||
x:Class="TR2X_Installer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>
|
21
tools/installer/TR2X_Installer/App.xaml.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Windows;
|
||||
using TR2X_Installer.Installers;
|
||||
using TRX_InstallerLib.Controls;
|
||||
using TRX_InstallerLib.Installers;
|
||||
|
||||
namespace TR2X_Installer;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public App()
|
||||
{
|
||||
Current.MainWindow = new TRXInstallWindow(new List<IInstallSource>
|
||||
{
|
||||
new SteamInstallSource(),
|
||||
new GOGInstallSource(),
|
||||
new CDRomInstallSource(),
|
||||
new TR2XInstallSource(),
|
||||
});
|
||||
Current.MainWindow.Show();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System.IO;
|
||||
|
||||
namespace TR2X_Installer.Installers;
|
||||
|
||||
public class CDRomInstallSource : GenericInstallSource
|
||||
{
|
||||
public override IEnumerable<string> DirectoriesToTry
|
||||
{
|
||||
get
|
||||
{
|
||||
DriveInfo[] allDrives = DriveInfo.GetDrives();
|
||||
foreach (var drive in allDrives)
|
||||
{
|
||||
if (drive.DriveType == DriveType.CDRom && drive.IsReady)
|
||||
{
|
||||
yield return drive.RootDirectory.FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsImportingSavesSupported => false;
|
||||
public override string SourceName => "CDRom";
|
||||
|
||||
public override bool IsGameFound(string sourceDirectory)
|
||||
{
|
||||
return File.Exists(Path.Combine(sourceDirectory, "fmv", "ancient.rpl"))
|
||||
&& File.Exists(Path.Combine(sourceDirectory, "data", "wall.tr2"))
|
||||
&& File.Exists(Path.Combine(sourceDirectory, "data", "main.sfx"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TR2X_Installer.Installers;
|
||||
|
||||
public class GOGInstallSource : GenericInstallSource
|
||||
{
|
||||
public override IEnumerable<string> DirectoriesToTry
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return @"C:\Program Files (x86)\GOG Galaxy\Games\Tomb Raider 2";
|
||||
|
||||
using var key = Registry.ClassesRoot.OpenSubKey(@"goggalaxy\shell\open\command");
|
||||
if (key is not null)
|
||||
{
|
||||
var value = key.GetValue("")?.ToString();
|
||||
if (value is not null && new Regex(@"""(?<path>[^""]+)""").Match(value) is { Success: true } match)
|
||||
{
|
||||
yield return Path.Combine(Path.GetDirectoryName(match.Groups["path"].Value)!, @"Games\Tomb Raider 2");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsImportingSavesSupported => true;
|
||||
public override string SourceName => "GOG";
|
||||
|
||||
public override bool IsGameFound(string sourceDirectory)
|
||||
{
|
||||
return File.Exists(Path.Combine(sourceDirectory, "tomb2.exe"))
|
||||
&& File.Exists(Path.Combine(sourceDirectory, "data", "wall.tr2"))
|
||||
&& File.Exists(Path.Combine(sourceDirectory, "data", "main.sfx"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using TRX_InstallerLib.Installers;
|
||||
using TRX_InstallerLib.Utils;
|
||||
|
||||
namespace TR2X_Installer.Installers;
|
||||
|
||||
public abstract class GenericInstallSource : BaseInstallSource
|
||||
{
|
||||
private static readonly Dictionary<string, List<string>> _targetFiles = new()
|
||||
{
|
||||
["data"] = new() { ".tr2", ".sfx", ".pcx" },
|
||||
["fmv"] = new() { ".*" },
|
||||
["music"] = new() { ".flac", ".ogg", ".mp3", ".wav" },
|
||||
};
|
||||
|
||||
public override bool IsDownloadingMusicNeeded(string sourceDirectory)
|
||||
{
|
||||
return !Directory.Exists(Path.Combine(sourceDirectory, "audio"))
|
||||
&& !Directory.Exists(Path.Combine(sourceDirectory, "music"));
|
||||
}
|
||||
|
||||
public override bool IsDownloadingExpansionNeeded(string sourceDirectory)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task CopyOriginalGameFiles(
|
||||
string sourceDirectory,
|
||||
string targetDirectory,
|
||||
IProgress<InstallProgress> progress,
|
||||
bool importSaves
|
||||
)
|
||||
{
|
||||
await InstallUtils.CopyDirectoryTree(
|
||||
sourceDirectory,
|
||||
targetDirectory,
|
||||
progress,
|
||||
file => IsMatch(sourceDirectory, file, importSaves),
|
||||
path => ConvertTargetPath(path)
|
||||
);
|
||||
|
||||
string musicDir = Path.Combine(targetDirectory, "music");
|
||||
string audioDir = Path.Combine(sourceDirectory, "audio");
|
||||
if ((Directory.Exists(musicDir) && Directory.EnumerateFiles(musicDir).Any()) || !Directory.Exists(audioDir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InstallUtils.CopyDirectoryTree(
|
||||
Path.Combine(sourceDirectory, "audio"),
|
||||
Path.Combine(targetDirectory, "audio"),
|
||||
progress,
|
||||
null,
|
||||
path => ConvertTargetPath(path)
|
||||
);
|
||||
}
|
||||
|
||||
private static bool IsMatch(string sourceDirectory, string path, bool importSaves)
|
||||
{
|
||||
string[] parts = Path.GetRelativePath(sourceDirectory, path).ToLower().Split('\\');
|
||||
if (parts.Length == 1 && importSaves && Regex.IsMatch(parts[0], @"savegame.\d+", RegexOptions.IgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return parts.Length > 0
|
||||
&& _targetFiles.ContainsKey(parts[0])
|
||||
&& (_targetFiles[parts[0]].Contains(".*") || _targetFiles[parts[0]].Contains(Path.GetExtension(path).ToLower()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
|
||||
namespace TR2X_Installer.Installers;
|
||||
|
||||
public class SteamInstallSource : GOGInstallSource
|
||||
{
|
||||
public override IEnumerable<string> DirectoriesToTry
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return @"C:\Program Files (x86)\Steam\steamapps\common\Tomb Raider (II)";
|
||||
|
||||
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Valve\Steam");
|
||||
if (key is not null)
|
||||
{
|
||||
var value = key.GetValue("SteamPath")?.ToString();
|
||||
if (value is not null)
|
||||
{
|
||||
yield return Path.Combine(value, @"steamapps\common\Tomb Raider (II)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string SourceName => "Steam";
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using TRX_InstallerLib.Installers;
|
||||
using TRX_InstallerLib.Utils;
|
||||
|
||||
namespace TR2X_Installer.Installers;
|
||||
|
||||
public class TR2XInstallSource : GenericInstallSource
|
||||
{
|
||||
public override IEnumerable<string> DirectoriesToTry
|
||||
{
|
||||
get
|
||||
{
|
||||
var previousPath = InstallUtils.GetPreviousInstallationPath();
|
||||
if (previousPath is not null)
|
||||
{
|
||||
yield return previousPath;
|
||||
}
|
||||
|
||||
foreach (var path in InstallUtils.GetDesktopShortcutDirectories())
|
||||
{
|
||||
yield return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string SuggestedInstallationDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return InstallUtils.GetPreviousInstallationPath() ?? base.SuggestedInstallationDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsImportingSavesSupported => true;
|
||||
public override string SourceName => "TR2X";
|
||||
|
||||
public override async Task CopyOriginalGameFiles(
|
||||
string sourceDirectory,
|
||||
string targetDirectory,
|
||||
IProgress<InstallProgress> progress,
|
||||
bool importSaves
|
||||
)
|
||||
{
|
||||
var filterRegex = new Regex(importSaves ? @"(audio|data|fmv|music|saves)[\\/]|save.*\.\d+" : @"(audio|data|fmv|music)[\\/]", RegexOptions.IgnoreCase);
|
||||
await InstallUtils.CopyDirectoryTree(
|
||||
sourceDirectory,
|
||||
targetDirectory,
|
||||
progress,
|
||||
file => filterRegex.IsMatch(file)
|
||||
);
|
||||
}
|
||||
|
||||
public override bool IsDownloadingExpansionNeeded(string sourceDirectory)
|
||||
{
|
||||
return !File.Exists(Path.Combine(sourceDirectory, "data", "title_gm.tr2"));
|
||||
}
|
||||
|
||||
public override bool IsGameFound(string sourceDirectory)
|
||||
{
|
||||
return File.Exists(Path.Combine(sourceDirectory, "TR2X.exe"));
|
||||
}
|
||||
}
|
BIN
tools/installer/TR2X_Installer/Resources/CDRom.png
Normal file
After Width: | Height: | Size: 163 KiB |
BIN
tools/installer/TR2X_Installer/Resources/GOG.png
Normal file
After Width: | Height: | Size: 20 KiB |
10
tools/installer/TR2X_Installer/Resources/Lang/en.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Controls": {
|
||||
"window_title_main": "TR2X Installer",
|
||||
"step_source_content": "TR2X requires original game files to run.\nPlease choose the source location where to install the data files from.\nIf you're upgrading an existing installation, please choose TR2X.",
|
||||
"step_settings_music_content": "This option lets you download compatible music files for the game automatically (60 MB). The legality of these files is disputable; the most legal way to import the music to PC is to obtain them from your own source - TR2 supports FLAC, OGG, MP3 and WAV files.",
|
||||
"step_settings_expansion_heading": "Download The Golden Mask expansion pack",
|
||||
"step_settings_expansion_content": "The Golden Mask expansion pack was made freeware. However, the Steam and GOG versions do not ship it. This option lets you download the expansion files automatically (15 MB).",
|
||||
"step_settings_saves_content": "Imports existing savegame files."
|
||||
}
|
||||
}
|
BIN
tools/installer/TR2X_Installer/Resources/Steam.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
tools/installer/TR2X_Installer/Resources/TR1X.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
tools/installer/TR2X_Installer/Resources/TR2X.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
tools/installer/TR2X_Installer/Resources/TombATI.png
Normal file
After Width: | Height: | Size: 21 KiB |
11
tools/installer/TR2X_Installer/Resources/const.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"Game": "TR2X",
|
||||
"GoldGame": "TR2X - GM",
|
||||
"GoldFileIdentifier": "title_gm.tr2",
|
||||
"AllowExpansionTypeSelection": false,
|
||||
"ShortcutTitle": "Tomb Raider II: Community Edition",
|
||||
"GoldZips": {
|
||||
"0": "trgm.zip",
|
||||
"1": "trgm.zip"
|
||||
}
|
||||
}
|
BIN
tools/installer/TR2X_Installer/Resources/icon.ico
Normal file
After Width: | Height: | Size: 175 KiB |
BIN
tools/installer/TR2X_Installer/Resources/release.zip
Normal file
BIN
tools/installer/TR2X_Installer/Resources/side1.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
tools/installer/TR2X_Installer/Resources/side2.jpg
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
tools/installer/TR2X_Installer/Resources/side3.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
tools/installer/TR2X_Installer/Resources/side4.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
53
tools/installer/TR2X_Installer/TR2X_Installer.csproj
Normal file
|
@ -0,0 +1,53 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<AssemblyName>TR2X_Installer</AssemblyName>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<EnableCompressionInSingleFile>false</EnableCompressionInSingleFile>
|
||||
<SelfContained>false</SelfContained>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TRX_InstallerLib\TRX_InstallerLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\const.json" />
|
||||
<None Remove="Resources\icon.ico" />
|
||||
<None Remove="Resources\Lang\en.json" />
|
||||
<None Remove="Resources\release.zip" />
|
||||
<None Remove="Resources\side1.jpg" />
|
||||
<None Remove="Resources\side2.jpg" />
|
||||
<None Remove="Resources\side3.jpg" />
|
||||
<None Remove="Resources\side4.jpg" />
|
||||
<None Remove="Resources\TR2X.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\CDRom.png" />
|
||||
<Resource Include="Resources\GOG.png" />
|
||||
<Resource Include="Resources\icon.ico" />
|
||||
<Resource Include="Resources\side1.jpg" />
|
||||
<Resource Include="Resources\side2.jpg" />
|
||||
<Resource Include="Resources\side3.jpg" />
|
||||
<Resource Include="Resources\side4.jpg" />
|
||||
<Resource Include="Resources\Steam.png" />
|
||||
<Resource Include="Resources\TR2X.png" />
|
||||
<EmbeddedResource Include="Resources\const.json" />
|
||||
<EmbeddedResource Include="Resources\Lang\en.json" />
|
||||
<EmbeddedResource Include="Resources\release.zip" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TRX_InstallerLib", "TRX_Ins
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TR1X_Installer", "TR1X_Installer\TR1X_Installer.csproj", "{5B32640D-3997-472F-A1BA-FCE4128E0688}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TR2X_Installer", "TR2X_Installer\TR2X_Installer.csproj", "{DCCEAD2D-BC68-40D7-B1B9-981450416466}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -21,6 +23,10 @@ Global
|
|||
{5B32640D-3997-472F-A1BA-FCE4128E0688}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5B32640D-3997-472F-A1BA-FCE4128E0688}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5B32640D-3997-472F-A1BA-FCE4128E0688}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DCCEAD2D-BC68-40D7-B1B9-981450416466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DCCEAD2D-BC68-40D7-B1B9-981450416466}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DCCEAD2D-BC68-40D7-B1B9-981450416466}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DCCEAD2D-BC68-40D7-B1B9-981450416466}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -54,6 +54,16 @@ public static class InstallUtils
|
|||
});
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
|
||||
await Task.Run(() => File.Copy(sourcePath, targetPath, true));
|
||||
|
||||
try
|
||||
{
|
||||
var file = new FileInfo(targetPath);
|
||||
if (file.Attributes.HasFlag(FileAttributes.ReadOnly))
|
||||
{
|
||||
file.IsReadOnly = false;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|