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);
return new Vector4(raw2 & 31, (raw2 >> 5) & 31, (raw2 >> 10) & 31, 31) / 31.0f;
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:
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]
public bool Dump = false;
InstallPaths _installPaths;
DbFile _file;
TextureLoader _textureLoader;
public override void _Ready()
{
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;
};
}
@ -88,7 +91,7 @@ public partial class Mission : Node3D
{
ClearMap();
_textureLoader = new TextureLoader("", FileName.GetBaseDir());
_textureLoader = new TextureLoader(_installPaths.famPath, FileName.GetBaseDir());
_file = new(FileName);
var textureList = (TxList)_file.Chunks["TXLIST"];

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Godot;
@ -7,21 +8,38 @@ namespace KeepersCompound.TMV;
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 Dictionary<string, string> _rootTexturePaths = new();
private readonly Dictionary<string, string> _fmTexturePaths = new();
private readonly List<ImageTexture> _textureCache = new();
private readonly Dictionary<int, int> _idMap = 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;
_userTexturesPath = ProjectSettings.GlobalizePath($"user://textures/tmp");
ExtractRootFamFiles();
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()
@ -32,7 +50,7 @@ public partial class TextureLoader
_textureCache.Add(texture);
}
private void RegisterFmTexturePaths()
private static void RegisterTexturePaths(string rootDir, Dictionary<string, string> map)
{
// TODO: Load DDS BMP GIF CEL
string[] validExtensions = { "png", "tga", "pcx" };
@ -44,33 +62,33 @@ public partial class TextureLoader
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))
{
if (validExtensions.Contains(path.GetExtension().ToLower()))
{
// TODO: This only adds the first one found rather than the highest priority
var key = path.TrimPrefix(_fmPath).GetBaseName().ToLower();
_fmTexturePaths.TryAdd(key, path);
var key = path.TrimPrefix(rootDir).GetBaseName().ToLower();
map.TryAdd(key, path);
}
}
}
GD.Print($"Registered {map.Count} texture paths at : {rootDir}");
}
public bool Load(int id, string path)
{
var userTexturesPath = ProjectSettings.GlobalizePath($"user://textures{path}.PCX");
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;
}
else if (File.Exists(userTexturesPath))
else if (_rootTexturePaths.TryGetValue(path.ToLower(), out var rootTexPath))
{
_textureCache.Add(LoadTexture(userTexturesPath));
_textureCache.Add(LoadTexture(rootTexPath));
loaded = true;
}

View File

@ -1,4 +1,5 @@
using System.IO;
using System.Linq;
using Godot;
namespace KeepersCompound.TMV.UI;
@ -6,28 +7,35 @@ namespace KeepersCompound.TMV.UI;
public partial class MissionSelector : Control
{
[Signal]
public delegate void LoadMissionEventHandler(string path);
public delegate void LoadMissionEventHandler(string rootPath, string missionPath);
private InstallPaths _installPaths;
private FileDialog _FolderSelect;
private LineEdit _FolderPath;
private Button _BrowseButton;
private ItemList _Campaigns;
private ItemList _Missions;
private Button _LoadButton;
private Button _CancelButton;
public override void _Ready()
{
// TODO: Load initial folderpath from config and prefil everything
_FolderSelect = GetNode<FileDialog>("%FolderSelect");
_FolderPath = GetNode<LineEdit>("%FolderPath");
_BrowseButton = GetNode<Button>("%BrowseButton");
_Campaigns = GetNode<ItemList>("%Campaigns");
_Missions = GetNode<ItemList>("%Missions");
_LoadButton = GetNode<Button>("%LoadButton");
_CancelButton = GetNode<Button>("%CancelButton");
_BrowseButton.Pressed += () => _FolderSelect.Visible = true;
_FolderSelect.DirSelected += (string dir) => { _FolderPath.Text = dir; BuildMissionList(dir); };
_FolderPath.TextSubmitted += BuildMissionList;
_FolderSelect.DirSelected += (string dir) => { _FolderPath.Text = dir; BuildCampaignList(dir); };
_FolderPath.TextSubmitted += BuildCampaignList;
_Missions.ItemSelected += (long _) => _LoadButton.Disabled = false;
_Campaigns.ItemSelected += BuildMissionList;
_LoadButton.Pressed += EmitLoadMission;
_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)
{
_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()
{
var selectedCampaign = _Campaigns.GetSelectedItems()[0];
var selected = _Missions.GetSelectedItems();
if (selected.IsEmpty())
{
return;
}
var item = _Missions.GetItemText(selected[0]);
var basePath = _FolderPath.Text;
var path = basePath + item;
EmitSignal(SignalName.LoadMission, path);
var campaignPath = selectedCampaign == 0 ? _installPaths.omsPath : _installPaths.fmsPath + _Campaigns.GetItemText(selectedCampaign);
var path = campaignPath + _Missions.GetItemText(selected[0]);
EmitSignal(SignalName.LoadMission, _installPaths.rootPath, path);
Visible = false;
}

View File

@ -6,7 +6,6 @@
font_size = 20
[node name="MissionSelector" type="Control"]
visible = false
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
@ -28,7 +27,7 @@ access = 2
use_native_dialog = true
[node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(460, 0)
custom_minimum_size = Vector2(640, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
@ -75,10 +74,20 @@ unique_name_in_owner = true
layout_mode = 2
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
custom_minimum_size = Vector2(0, 480)
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"]
layout_mode = 2