Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instanced Projects, AssetLocator, ParamBank, FMGBank #895

Merged
merged 43 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
03ae5ea
Create project replacing assetlocator, rework functions to use common…
Philiquaz Apr 29, 2024
4ea5fd6
Move statics usages
Philiquaz Apr 29, 2024
2950aca
Remove passing of AssetLocator - Make it (temporarily) static
Philiquaz Apr 29, 2024
6c42a46
Turn Assetlocator into a static dummy
Philiquaz Apr 29, 2024
68afcf0
Move static hack to AssetLocator
Philiquaz Apr 29, 2024
9218d1a
Rename project to projectassetlocator, introduce owning project object
Philiquaz Apr 29, 2024
b09337d
Clean some imports
Philiquaz Apr 29, 2024
522aded
END SUPPORT of ER PartialParams, ParamBank to use new assetlocator.
Philiquaz May 7, 2024
cfc43f9
Stop reusing parambank instances, make them part of Project. Also kee…
Philiquaz May 10, 2024
eec8a6a
Change comparison menus to select project/mod folder
Philiquaz May 10, 2024
e08fe37
Allow to still select json from compare, including loading settings f…
Philiquaz May 10, 2024
dccb513
Defs loaded on a per-project/bank basis
Philiquaz May 11, 2024
28567cf
Merge remote-tracking branch 'Soulsmods/master' into feat/Projects
Philiquaz May 11, 2024
563ccf1
Repair from merge
Philiquaz May 11, 2024
509024a
Merge branch 'feat/Projects' into feat/ProjectsPt2
Philiquaz May 11, 2024
59d44a9
Move parammeta to be part of parambank
Philiquaz May 12, 2024
c0cb605
deduplicate project gametype (again)
Philiquaz May 22, 2024
f6d09c8
undo privacy because we are setting it outside and I don't want to fi…
Philiquaz May 22, 2024
e3dccc2
Scuffed start on instanced FMGBank
Philiquaz May 22, 2024
a58e6fd
Use own assetlocator in fmgbank, fix writemode
Philiquaz May 22, 2024
24abf0e
fmg loading with fromOptions
Philiquaz May 22, 2024
4f25dae
Move FMG enums to their own class
Philiquaz May 22, 2024
64d97b6
Move big switches
Philiquaz May 22, 2024
6980550
move post-load fmginfo edits
Philiquaz May 24, 2024
fccf0c2
Renamed UICategories to FileCategories so that we're not putting UI s…
Philiquaz Jun 3, 2024
aa21aef
Move data into dictionary rather than list
Philiquaz Jun 3, 2024
bdd38db
rename fmginfo uicategory, filecategory.text
Philiquaz Jun 3, 2024
014853d
Move the game-based hacks to generateInfo
Philiquaz Jun 3, 2024
ab250b9
naming and insertion consistency
Philiquaz Jun 3, 2024
d0ad25c
Loaded categories is now backed by the fmgbank itself
Philiquaz Jun 3, 2024
a6c2056
Patching is on lookup instead of modifying data
Philiquaz Jun 3, 2024
88476fc
Move sorting logic to lookup stuff
Philiquaz Jun 3, 2024
f049647
Split up Exporter, EntryGroup and FMGInfo
Philiquaz Jun 3, 2024
d5fc007
no more partial class
Philiquaz Jun 3, 2024
f5e32c0
Move reloading of FMGs to bank and outside of screen.
Philiquaz Jun 3, 2024
830fe86
Move most of FMGBank into FMGLang
Philiquaz Jun 3, 2024
efec21f
Introduce FMGFileSet, finally some semblence of functioning again.
Philiquaz Jun 4, 2024
b9f0c7e
Merge remote-tracking branch 'Soulsmods/master' into feat/ProjectsFMG…
Philiquaz Jun 4, 2024
93e77c9
Fix Errors when no project loaded, assetbrowser ordering
Philiquaz Jun 4, 2024
4b0ae66
Merge remote-tracking branch 'Soulsmods/master' into feat/ProjectsFMG…
Philiquaz Jun 4, 2024
e9b45a0
GetProjectFileAllPaths util
Philiquaz Jun 16, 2024
df5d439
Merge remote-tracking branch 'Soulsmods/master' into feat/ProjectsFMG…
Philiquaz Jun 29, 2024
263fb66
merge compile fixes
Philiquaz Jun 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,223 changes: 70 additions & 2,153 deletions src/StudioCore/AssetLocator.cs

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions src/StudioCore/AssetUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using SoulsFormats;
using StudioCore.Editor;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace StudioCore;

/// <summary>
/// Helper functions and statics for Project/Assetlocator
/// </summary>
public class AssetUtils
{
public static readonly string GameExecutableFilter;
public static readonly string ProjectJsonFilter;
public static readonly string RegulationBinFilter;
public static readonly string Data0Filter;
public static readonly string ParamBndDcxFilter;
public static readonly string ParamBndFilter;
public static readonly string EncRegulationFilter;
public static readonly string ParamLooseFilter;
public static readonly string CsvFilter;
public static readonly string TxtFilter;
public static readonly string FmgJsonFilter;

static AssetUtils()
{
// These patterns are meant to be passed directly into PlatformUtils.
// Everything about their handling should be done there.

// Game Executable (.EXE, EBOOT.BIN)|*.EXE*;*EBOOT.BIN*
// Windows executable (*.EXE)|*.EXE*
// Playstation executable (*.BIN)|*.BIN*
GameExecutableFilter = "exe,bin";
// Project file (project.json)|PROJECT.JSON
ProjectJsonFilter = "json";
// Regulation file (regulation.bin)|REGULATION.BIN
RegulationBinFilter = "bin";
// Data file (Data0.bdt)|DATA0.BDT
Data0Filter = "bdt";
// ParamBndDcx (gameparam.parambnd.dcx)|GAMEPARAM.PARAMBND.DCX
ParamBndDcxFilter = "parambnd.dcx";
// ParamBnd (gameparam.parambnd)|GAMEPARAM.PARAMBND
ParamBndFilter = "parambnd";
// Enc_RegBndDcx (enc_regulation.bnd.dcx)|ENC_REGULATION.BND.DCX
EncRegulationFilter = "bnd.dcx";
// Loose Param file (*.Param)|*.Param
ParamLooseFilter = "param";
// CSV file (*.csv)|*.csv
CsvFilter = "csv";
// Text file (*.txt)|*.txt
TxtFilter = "txt";
// Exported FMGs (*.fmg.json)|*.fmg.json
FmgJsonFilter = "fmg.json";
// All file filter is implicitly added by NFD. Ideally this is used explicitly.
// All files|*.*
}
public static GameType GetGameTypeForExePath(string exePath)
{
var type = GameType.Undefined;
if (exePath.ToLower().Contains("darksouls.exe"))
Copy link

@lofcz lofcz Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a static dictionary for a fast lookup? (for all the switches here, might add up if summed)

Copy link
Collaborator Author

@Philiquaz Philiquaz Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might, but then there's the nuanced cases like des vs bb, or in other such methods the shared cases.
For the whole file, I think ideally we'd define an object/jumptable and do some scummy OOP to handle per-game behaviours (and reuse this "game implementation" class for different databanks eg param loading code, fmg loading code etc.)
But moving to that sort of system might be a step beyond what this PR is focusing on, which is more to instantiate projects and databanks separately, as well as just clean up some asset lookup code.

That is, if I'm understanding your point.
In terms of performance, this particular function is negligibly used.

{
type = GameType.DarkSoulsPTDE;
}
else if (exePath.ToLower().Contains("darksoulsremastered.exe"))
{
type = GameType.DarkSoulsRemastered;
}
else if (exePath.ToLower().Contains("darksoulsii.exe"))
{
type = GameType.DarkSoulsIISOTFS;
}
else if (exePath.ToLower().Contains("darksoulsiii.exe"))
{
type = GameType.DarkSoulsIII;
}
else if (exePath.ToLower().Contains("eboot.bin"))
{
var path = Path.GetDirectoryName(exePath);
if (Directory.Exists($@"{path}\dvdroot_ps4"))
{
type = GameType.Bloodborne;
}
else
{
type = GameType.DemonsSouls;
}
}
else if (exePath.ToLower().Contains("sekiro.exe"))
{
type = GameType.Sekiro;
}
else if (exePath.ToLower().Contains("eldenring.exe"))
{
type = GameType.EldenRing;
}
else if (exePath.ToLower().Contains("armoredcore6.exe"))
{
type = GameType.ArmoredCoreVI;
}

return type;
}

public static bool CheckFilesExpanded(string gamepath, GameType game)
{
if (game == GameType.EldenRing)
{
if (!Directory.Exists($@"{gamepath}\map"))
{
return false;
}

if (!Directory.Exists($@"{gamepath}\asset"))
{
return false;
}
}

if (game is GameType.DarkSoulsPTDE or GameType.DarkSoulsIII or GameType.Sekiro)
{
if (!Directory.Exists($@"{gamepath}\map"))
{
return false;
}

if (!Directory.Exists($@"{gamepath}\obj"))
{
return false;
}
}

if (game == GameType.DarkSoulsIISOTFS)
{
if (!Directory.Exists($@"{gamepath}\map"))
{
return false;
}

if (!Directory.Exists($@"{gamepath}\model\obj"))
{
return false;
}
}

if (game == GameType.ArmoredCoreVI)
{
if (!Directory.Exists($@"{gamepath}\map"))
{
return false;
}

if (!Directory.Exists($@"{gamepath}\asset"))
{
return false;
}
}

return true;
}

public static string GetGameIDForDir(GameType Type)
{
switch (Type)
{
case GameType.DemonsSouls:
return "DES";
case GameType.DarkSoulsPTDE:
return "DS1";
case GameType.DarkSoulsRemastered:
return "DS1R";
case GameType.DarkSoulsIISOTFS:
return "DS2S";
case GameType.Bloodborne:
return "BB";
case GameType.DarkSoulsIII:
return "DS3";
case GameType.Sekiro:
return "SDT";
case GameType.EldenRing:
return "ER";
case GameType.ArmoredCoreVI:
return "AC6";
default:
throw new Exception("Game type not set");
}
}

public static AssetDescription GetNullAsset()
{
AssetDescription ret = new();
ret.AssetPath = "null";
ret.AssetName = "null";
ret.AssetArchiveVirtualPath = "null";
ret.AssetVirtualPath = "null";
return ret;
}

public static string GetBinderVirtualPath(string virtualPathToBinder, string binderFilePath)
{
var filename = Path.GetFileNameWithoutExtension($@"{binderFilePath}.blah");
if (filename.Length > 0)
{
filename = $@"{virtualPathToBinder}/{filename}";
}
else
{
filename = virtualPathToBinder;
}

return filename;
}
}
12 changes: 6 additions & 6 deletions src/StudioCore/Editor/Action.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,10 @@ public override ActionEvent Undo()

public class DuplicateFMGEntryAction : EditorAction
{
private readonly FMGBank.EntryGroup EntryGroup;
private FMGBank.EntryGroup NewEntryGroup;
private readonly FMGEntryGroup EntryGroup;
private FMGEntryGroup NewEntryGroup;

public DuplicateFMGEntryAction(FMGBank.EntryGroup entryGroup)
public DuplicateFMGEntryAction(FMGEntryGroup entryGroup)
{
EntryGroup = entryGroup;
}
Expand All @@ -333,10 +333,10 @@ public override ActionEvent Undo()

public class DeleteFMGEntryAction : EditorAction
{
private FMGBank.EntryGroup BackupEntryGroup = new();
private FMGBank.EntryGroup EntryGroup;
private FMGEntryGroup BackupEntryGroup = new();
private FMGEntryGroup EntryGroup;

public DeleteFMGEntryAction(FMGBank.EntryGroup entryGroup)
public DeleteFMGEntryAction(FMGEntryGroup entryGroup)
{
EntryGroup = entryGroup;
}
Expand Down
22 changes: 11 additions & 11 deletions src/StudioCore/Editor/EditorDecorations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,21 +259,21 @@ public static void ParamRefsSelectables(ParamBank bank, List<ParamRef> paramRefs
return rows;
}

private static List<(string, FMGBank.EntryGroup)> resolveFMGRefs(List<FMGRef> fmgRefs, Param.Row context,
private static List<(string, FMGEntryGroup)> resolveFMGRefs(List<FMGRef> fmgRefs, Param.Row context,
dynamic oldval)
{
if (!FMGBank.IsLoaded)
if (!Locator.ActiveProject.FMGBank.IsLoaded)
{
return new List<(string, FMGBank.EntryGroup)>();
return new List<(string, FMGEntryGroup)>();
}

return fmgRefs.Where(rf =>
{
Param.Cell? c = context?[rf.conditionField];
return context == null || c == null || Convert.ToInt32(c.Value.Value) == rf.conditionValue;
}).Select(rf => FMGBank.FmgInfoBank.Find(x => x.Name == rf.fmg))
}).Select(rf => Locator.ActiveProject.FMGBank.FmgInfoBank.FirstOrDefault(x => x.Name == rf.fmg))
.Where(fmgi => fmgi != null)
.Select(fmgi => (fmgi.Name, FMGBank.GenerateEntryGroup((int)oldval, fmgi)))
.Select(fmgi => (fmgi.Name, Locator.ActiveProject.FMGBank.GenerateEntryGroup((int)oldval, fmgi)))
.ToList();
}

Expand All @@ -282,11 +282,11 @@ public static void FmgRefSelectable(EditorScreen ownerScreen, List<FMGRef> fmgNa
{
List<string> textsToPrint = UICache.GetCached(ownerScreen, (int)oldval, "PARAM META FMGREF", () =>
{
List<(string, FMGBank.EntryGroup)> refs = resolveFMGRefs(fmgNames, context, oldval);
List<(string, FMGEntryGroup)> refs = resolveFMGRefs(fmgNames, context, oldval);
return refs.Where(x => x.Item2 != null)
.Select(x =>
{
FMGBank.EntryGroup group = x.Item2;
FMGEntryGroup group = x.Item2;
var toPrint = "";
if (!string.IsNullOrWhiteSpace(group.Title?.Text))
{
Expand Down Expand Up @@ -379,7 +379,7 @@ public static void VirtualParamRefSelectables(ParamBank bank, string virtualRefN
{
List<string> matchedExtRefPath =
currentRef.paths.Select(x => string.Format(x, searchValue)).ToList();
AssetLocator al = ParamBank.PrimaryBank.AssetLocator;
AssetLocator al = Locator.AssetLocator;
ExtRefItem(context, fieldName, $"modded {currentRef.name}", matchedExtRefPath, al.GameModDirectory,
cacheOwner);
ExtRefItem(context, fieldName, $"vanilla {currentRef.name}", matchedExtRefPath,
Expand Down Expand Up @@ -464,7 +464,7 @@ public static void ParamRefEnumQuickLink(ParamBank bank, object oldval, List<Par
}
else if (fmgRefs != null)
{
(string, FMGBank.EntryGroup)? primaryRef =
(string, FMGEntryGroup)? primaryRef =
resolveFMGRefs(fmgRefs, context, oldval)?.FirstOrDefault();
if (primaryRef?.Item2 != null)
{
Expand Down Expand Up @@ -599,9 +599,9 @@ public static void PropertyRowFMGRefsContextItems(List<FMGRef> reftypes, Param.R
ActionManager executor)
{
// Add Goto statements
List<(string, FMGBank.EntryGroup)> refs = resolveFMGRefs(reftypes, context, oldval);
List<(string, FMGEntryGroup)> refs = resolveFMGRefs(reftypes, context, oldval);
var ctrlDown = InputTracker.GetKey(Key.ControlLeft) || InputTracker.GetKey(Key.ControlRight);
foreach ((var name, FMGBank.EntryGroup group) in refs)
foreach ((var name, FMGEntryGroup group) in refs)
{
if (ImGui.Selectable($@"Goto {name} Text"))
{
Expand Down
23 changes: 20 additions & 3 deletions src/StudioCore/Editor/ProjectSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using StudioCore.Platform;
using Silk.NET.Core;
using StudioCore.Platform;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -39,8 +40,6 @@ public class ProjectSettings
/// </summary>
public bool UseLooseParams { get; set; } = false;

public bool PartialParams { get; set; } = false;

// FMG editor
public string LastFmgLanguageUsed { get; set; } = "";

Expand Down Expand Up @@ -79,6 +78,24 @@ public static ProjectSettings Deserialize(string path)
return null;
}
}

internal ProjectSettings CopyAndAssumeFromModDir(string moddir)
{
ProjectSettings newProj = new();
newProj.ProjectName = Path.GetFileName(Path.GetDirectoryName(moddir));
newProj.GameRoot = GameRoot;
newProj.GameType = GameType;
switch (newProj.GameType)
{
case GameType.DarkSoulsIISOTFS:
newProj.UseLooseParams = File.Exists($@"{moddir}\Param\AreaParam.param");
break;
case GameType.DarkSoulsIII:
newProj.UseLooseParams = File.Exists($@"{moddir}\param\gameparam\gameparam_dlc2.parambnd.dcx");
break;
}
return newProj;
}
}

public class NewProjectOptions
Expand Down
4 changes: 2 additions & 2 deletions src/StudioCore/Interface/AliasUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ public static string FindPlayerCharacterName(Entity e, string modelName)
var term = c.Value.ToParamEditorString();
var result = term;

if (FMGBank.IsLoaded)
if (Locator.ActiveProject.FMGBank.IsLoaded)
{
var matchingFmgInfo = FMGBank.FmgInfoBank.Find(x => x.Name.Contains("Character"));
var matchingFmgInfo = Locator.ActiveProject.FMGBank.FmgInfoBank.First(x => x.Name.Contains("Character"));

if (matchingFmgInfo != null)
{
Expand Down
Loading
Loading