Compare commits

...

4 Commits

6 changed files with 163 additions and 28 deletions

View File

@ -134,7 +134,8 @@ public class WorldRep : IChunk
var raw2 = Pixels[layer, y, x, 0] + (Pixels[layer, y, x, 1] << 8); var raw2 = Pixels[layer, y, x, 0] + (Pixels[layer, y, x, 1] << 8);
return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f; return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f;
case 4: case 4:
return new Vector4(Pixels[layer, y, x, 0], Pixels[layer, y, x, 1], Pixels[layer, y, x, 2], Pixels[layer, y, x, 3]) / 255.0f; // 4bpp stored as BGRA
return new Vector4(Pixels[layer, y, x, 2], Pixels[layer, y, x, 1], Pixels[layer, y, x, 0], Pixels[layer, y, x, 3]) / 255.0f;
default: default:
return Vector4.Zero; return Vector4.Zero;
} }

View File

@ -0,0 +1,55 @@
using System.IO;
using Godot;
namespace KeepersCompound.TMV;
// TODO: Error handling lol
public class InstallPaths
{
public string rootPath;
public string famPath;
public string omsPath;
public string fmsPath;
public InstallPaths(string root)
{
var searchOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true
};
rootPath = root;
famPath = Directory.GetFiles(rootPath, "fam.crf", searchOptions)[0];
var paths = Directory.GetFiles(rootPath, "install.cfg", searchOptions);
if (paths.Length == 0)
{
paths = Directory.GetFiles(rootPath, "darkinst.cfg", searchOptions);
}
var installCfgPath = paths[0];
GD.Print($"Install.cfg: {installCfgPath}");
foreach (var line in File.ReadLines(installCfgPath))
{
if (line.StartsWith("load_path"))
{
var path = line.Split(" ")[1].Replace("\\", "/");
omsPath = Path.GetFullPath(rootPath + path);
break;
}
}
var camModPath = Directory.GetFiles(rootPath, "cam_mod.ini", searchOptions)[0];
fmsPath = rootPath + "/FMs";
foreach (var line in File.ReadLines(camModPath))
{
if (line.StartsWith("fm_path"))
{
var path = line.Split(" ")[1].Replace("\\", "/");
fmsPath = Path.GetFullPath(rootPath + path);
break;
}
}
GD.Print($"OMs Path: {omsPath}");
}
}

View File

@ -38,15 +38,18 @@ public partial class Mission : Node3D
[Export] [Export]
public bool Dump = false; public bool Dump = false;
InstallPaths _installPaths;
DbFile _file; DbFile _file;
TextureLoader _textureLoader; TextureLoader _textureLoader;
public override void _Ready() public override void _Ready()
{ {
var missionSelector = GetNode<Control>("%MissionSelector") as MissionSelector; var missionSelector = GetNode<Control>("%MissionSelector") as MissionSelector;
missionSelector.LoadMission += (string path) => missionSelector.LoadMission += (string rootPath, string missionPath) =>
{ {
FileName = path; _installPaths = new InstallPaths(rootPath);
FileName = missionPath;
Build = true; Build = true;
}; };
} }
@ -88,7 +91,7 @@ public partial class Mission : Node3D
{ {
ClearMap(); ClearMap();
_textureLoader = new TextureLoader("", FileName.GetBaseDir()); _textureLoader = new TextureLoader(_installPaths.famPath, FileName.GetBaseDir());
_file = new(FileName); _file = new(FileName);
var textureList = (TxList)_file.Chunks["TXLIST"]; var textureList = (TxList)_file.Chunks["TXLIST"];

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using Godot; using Godot;
@ -7,21 +8,38 @@ namespace KeepersCompound.TMV;
public partial class TextureLoader public partial class TextureLoader
{ {
private readonly string _rootPath; // TODO: Load from installation resources private readonly string _userTexturesPath;
private readonly string _rootFamPath; // TODO: Load from installation resources
private readonly string _fmPath; private readonly string _fmPath;
private readonly Dictionary<string, string> _rootTexturePaths = new();
private readonly Dictionary<string, string> _fmTexturePaths = new(); private readonly Dictionary<string, string> _fmTexturePaths = new();
private readonly List<ImageTexture> _textureCache = new(); private readonly List<ImageTexture> _textureCache = new();
private readonly Dictionary<int, int> _idMap = new(); private readonly Dictionary<int, int> _idMap = new();
private readonly Dictionary<string, int> _pathMap = new(); private readonly Dictionary<string, int> _pathMap = new();
public TextureLoader(string rootPath, string fmPath) public TextureLoader(string rootFamPath, string fmPath)
{ {
_rootPath = rootPath; _rootFamPath = rootFamPath;
_fmPath = fmPath; _fmPath = fmPath;
_userTexturesPath = ProjectSettings.GlobalizePath($"user://textures/tmp");
ExtractRootFamFiles();
LoadDefaultTexture(); LoadDefaultTexture();
RegisterFmTexturePaths(); RegisterTexturePaths(_fmPath, _fmTexturePaths);
RegisterTexturePaths(_userTexturesPath, _rootTexturePaths);
}
private void ExtractRootFamFiles()
{
var dir = new DirectoryInfo(_userTexturesPath);
if (dir.Exists)
{
dir.Delete(true);
}
var zip = ZipFile.OpenRead(_rootFamPath);
zip.ExtractToDirectory(_userTexturesPath.PathJoin("fam"));
} }
private void LoadDefaultTexture() private void LoadDefaultTexture()
@ -32,7 +50,7 @@ public partial class TextureLoader
_textureCache.Add(texture); _textureCache.Add(texture);
} }
private void RegisterFmTexturePaths() private static void RegisterTexturePaths(string rootDir, Dictionary<string, string> map)
{ {
// TODO: Load DDS BMP GIF CEL // TODO: Load DDS BMP GIF CEL
string[] validExtensions = { "png", "tga", "pcx" }; string[] validExtensions = { "png", "tga", "pcx" };
@ -44,33 +62,33 @@ public partial class TextureLoader
RecurseSubdirectories = true, RecurseSubdirectories = true,
}; };
foreach (var dirPath in Directory.EnumerateDirectories(_fmPath, "fam", famOptions)) foreach (var dirPath in Directory.EnumerateDirectories(rootDir, "fam", famOptions))
{ {
foreach (var path in Directory.EnumerateFiles(dirPath, "*", textureOptions)) foreach (var path in Directory.EnumerateFiles(dirPath, "*", textureOptions))
{ {
if (validExtensions.Contains(path.GetExtension().ToLower())) if (validExtensions.Contains(path.GetExtension().ToLower()))
{ {
// TODO: This only adds the first one found rather than the highest priority // TODO: This only adds the first one found rather than the highest priority
var key = path.TrimPrefix(_fmPath).GetBaseName().ToLower(); var key = path.TrimPrefix(rootDir).GetBaseName().ToLower();
_fmTexturePaths.TryAdd(key, path); map.TryAdd(key, path);
} }
} }
} }
GD.Print($"Registered {map.Count} texture paths at : {rootDir}");
} }
public bool Load(int id, string path) public bool Load(int id, string path)
{ {
var userTexturesPath = ProjectSettings.GlobalizePath($"user://textures{path}.PCX");
var loaded = false; var loaded = false;
if (_fmTexturePaths.TryGetValue(path.ToLower(), out var filePath)) if (_fmTexturePaths.TryGetValue(path.ToLower(), out var fmTexPath))
{ {
_textureCache.Add(LoadTexture(filePath)); _textureCache.Add(LoadTexture(fmTexPath));
loaded = true; loaded = true;
} }
else if (File.Exists(userTexturesPath)) else if (_rootTexturePaths.TryGetValue(path.ToLower(), out var rootTexPath))
{ {
_textureCache.Add(LoadTexture(userTexturesPath)); _textureCache.Add(LoadTexture(rootTexPath));
loaded = true; loaded = true;
} }

View File

@ -1,4 +1,5 @@
using System.IO; using System.IO;
using System.Linq;
using Godot; using Godot;
namespace KeepersCompound.TMV.UI; namespace KeepersCompound.TMV.UI;
@ -6,28 +7,35 @@ namespace KeepersCompound.TMV.UI;
public partial class MissionSelector : Control public partial class MissionSelector : Control
{ {
[Signal] [Signal]
public delegate void LoadMissionEventHandler(string path); public delegate void LoadMissionEventHandler(string rootPath, string missionPath);
private InstallPaths _installPaths;
private FileDialog _FolderSelect; private FileDialog _FolderSelect;
private LineEdit _FolderPath; private LineEdit _FolderPath;
private Button _BrowseButton; private Button _BrowseButton;
private ItemList _Campaigns;
private ItemList _Missions; private ItemList _Missions;
private Button _LoadButton; private Button _LoadButton;
private Button _CancelButton; private Button _CancelButton;
public override void _Ready() public override void _Ready()
{ {
// TODO: Load initial folderpath from config and prefil everything
_FolderSelect = GetNode<FileDialog>("%FolderSelect"); _FolderSelect = GetNode<FileDialog>("%FolderSelect");
_FolderPath = GetNode<LineEdit>("%FolderPath"); _FolderPath = GetNode<LineEdit>("%FolderPath");
_BrowseButton = GetNode<Button>("%BrowseButton"); _BrowseButton = GetNode<Button>("%BrowseButton");
_Campaigns = GetNode<ItemList>("%Campaigns");
_Missions = GetNode<ItemList>("%Missions"); _Missions = GetNode<ItemList>("%Missions");
_LoadButton = GetNode<Button>("%LoadButton"); _LoadButton = GetNode<Button>("%LoadButton");
_CancelButton = GetNode<Button>("%CancelButton"); _CancelButton = GetNode<Button>("%CancelButton");
_BrowseButton.Pressed += () => _FolderSelect.Visible = true; _BrowseButton.Pressed += () => _FolderSelect.Visible = true;
_FolderSelect.DirSelected += (string dir) => { _FolderPath.Text = dir; BuildMissionList(dir); }; _FolderSelect.DirSelected += (string dir) => { _FolderPath.Text = dir; BuildCampaignList(dir); };
_FolderPath.TextSubmitted += BuildMissionList; _FolderPath.TextSubmitted += BuildCampaignList;
_Missions.ItemSelected += (long _) => _LoadButton.Disabled = false; _Missions.ItemSelected += (long _) => _LoadButton.Disabled = false;
_Campaigns.ItemSelected += BuildMissionList;
_LoadButton.Pressed += EmitLoadMission; _LoadButton.Pressed += EmitLoadMission;
_CancelButton.Pressed += () => Visible = false; _CancelButton.Pressed += () => Visible = false;
} }
@ -43,6 +51,21 @@ public partial class MissionSelector : Control
} }
} }
private void BuildCampaignList(string path)
{
_installPaths = new InstallPaths(path);
_Campaigns.Clear();
_Missions.Clear();
_LoadButton.Disabled = true;
_Campaigns.AddItem("Original Missions");
foreach (var c in Directory.GetDirectories(_installPaths.fmsPath))
{
_Campaigns.AddItem(c.TrimPrefix(_installPaths.fmsPath));
}
}
private void BuildMissionList(string path) private void BuildMissionList(string path)
{ {
_Missions.Clear(); _Missions.Clear();
@ -56,18 +79,44 @@ public partial class MissionSelector : Control
} }
} }
private void BuildMissionList(long idx)
{
_Missions.Clear();
_LoadButton.Disabled = true;
var campaignPath = "";
if (idx == 0)
{
campaignPath = _installPaths.omsPath;
}
else
{
campaignPath = _installPaths.fmsPath + _Campaigns.GetItemText((int)idx);
}
string[] extensions = { "mis", "cow" };
campaignPath = idx == 0 ? _installPaths.omsPath : _installPaths.fmsPath + _Campaigns.GetItemText((int)idx);
foreach (var f in Directory.GetFiles(campaignPath))
{
if (extensions.Contains(f.GetExtension().ToLower()))
{
_Missions.AddItem(f.TrimPrefix(campaignPath));
}
}
}
private void EmitLoadMission() private void EmitLoadMission()
{ {
var selectedCampaign = _Campaigns.GetSelectedItems()[0];
var selected = _Missions.GetSelectedItems(); var selected = _Missions.GetSelectedItems();
if (selected.IsEmpty()) if (selected.IsEmpty())
{ {
return; return;
} }
var item = _Missions.GetItemText(selected[0]); var campaignPath = selectedCampaign == 0 ? _installPaths.omsPath : _installPaths.fmsPath + _Campaigns.GetItemText(selectedCampaign);
var basePath = _FolderPath.Text; var path = campaignPath + _Missions.GetItemText(selected[0]);
var path = basePath + item; EmitSignal(SignalName.LoadMission, _installPaths.rootPath, path);
EmitSignal(SignalName.LoadMission, path);
Visible = false; Visible = false;
} }

View File

@ -6,7 +6,6 @@
font_size = 20 font_size = 20
[node name="MissionSelector" type="Control"] [node name="MissionSelector" type="Control"]
visible = false
layout_mode = 3 layout_mode = 3
anchors_preset = 8 anchors_preset = 8
anchor_left = 0.5 anchor_left = 0.5
@ -28,7 +27,7 @@ access = 2
use_native_dialog = true use_native_dialog = true
[node name="PanelContainer" type="PanelContainer" parent="."] [node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(460, 0) custom_minimum_size = Vector2(640, 0)
layout_mode = 1 layout_mode = 1
anchors_preset = 8 anchors_preset = 8
anchor_left = 0.5 anchor_left = 0.5
@ -75,10 +74,20 @@ unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Browse" text = "Browse"
[node name="Missions" type="ItemList" parent="PanelContainer/MarginContainer/VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Campaigns" type="ItemList" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(0, 480) custom_minimum_size = Vector2(0, 480)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3
[node name="Missions" type="ItemList" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 480)
layout_mode = 2
size_flags_horizontal = 3
[node name="Buttons" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] [node name="Buttons" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2 layout_mode = 2