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

Restructure user directory, refactor for testability, add a ton of tests #36

Merged
merged 10 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# Archive files used for testing
bgm/
vce/
dat.bin
evt.bin
grp.bin

# User-specific files
*.rsuser
*.suo
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Please file bugs in the Issues tab in this repository. Please include the follow
* The platform you are running Serial Loops on
* The version of the Chokuretsu ROM you are using (Japanese, patched English ROM, etc.)
* A description of the steps required to reproduce the issue
* The relevant logs for the issue (can be found in ~/SerialLoops/logs)
* The relevant logs for the issue (can be found in ~/SerialLoops/Logs)
23 changes: 19 additions & 4 deletions SerialLoops.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerialLoops.Tests", "test\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerialLoops.Lib", "src\SerialLoops.Lib\SerialLoops.Lib.csproj", "{CC729174-57CD-499A-B6E7-1FA6F9CE255F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialLoops.Gtk", "src\SerialLoops.Gtk\SerialLoops.Gtk.csproj", "{EA5F17A9-CA95-43A0-BE14-33F41EDF02BB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerialLoops.Gtk", "src\SerialLoops.Gtk\SerialLoops.Gtk.csproj", "{EA5F17A9-CA95-43A0-BE14-33F41EDF02BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialLoops.Mac", "src\SerialLoops.Mac\SerialLoops.Mac.csproj", "{2DF676C8-9917-48EA-A8CF-435CE29BB8A8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerialLoops.Mac", "src\SerialLoops.Mac\SerialLoops.Mac.csproj", "{2DF676C8-9917-48EA-A8CF-435CE29BB8A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialLoops.Wpf", "src\SerialLoops.Wpf\SerialLoops.Wpf.csproj", "{2DF0B35B-5A1C-43C7-A6BD-95D4D48FE39A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SerialLoops.Wpf", "src\SerialLoops.Wpf\SerialLoops.Wpf.csproj", "{2DF0B35B-5A1C-43C7-A6BD-95D4D48FE39A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{79884E84-D537-402C-BC5F-33F2B9ED56A0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1F8886B6-CAA8-4A7C-BC62-56F591290E02}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "platforms", "platforms", "{D63057DD-B2DB-49A0-8680-0CDBE6268550}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -49,8 +55,17 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E049F009-138F-401A-9D5A-1977CB8DAF49} = {79884E84-D537-402C-BC5F-33F2B9ED56A0}
{0B5D20CC-9E91-4BC0-8A6E-0342ADCAC48D} = {1F8886B6-CAA8-4A7C-BC62-56F591290E02}
{CC729174-57CD-499A-B6E7-1FA6F9CE255F} = {79884E84-D537-402C-BC5F-33F2B9ED56A0}
{EA5F17A9-CA95-43A0-BE14-33F41EDF02BB} = {D63057DD-B2DB-49A0-8680-0CDBE6268550}
{2DF676C8-9917-48EA-A8CF-435CE29BB8A8} = {D63057DD-B2DB-49A0-8680-0CDBE6268550}
{2DF0B35B-5A1C-43C7-A6BD-95D4D48FE39A} = {D63057DD-B2DB-49A0-8680-0CDBE6268550}
{D63057DD-B2DB-49A0-8680-0CDBE6268550} = {79884E84-D537-402C-BC5F-33F2B9ED56A0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A7259B55-BEEA-4ECC-AC0A-85CC8FA1BD53}
SolutionGuid = {922A1BAC-5F5E-4225-8E10-69036C42B786}
SolutionGuid = {A7259B55-BEEA-4ECC-AC0A-85CC8FA1BD53}
EndGlobalSection
EndGlobal
12 changes: 8 additions & 4 deletions src/SerialLoops.Lib/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SerialLoops.Lib
{
public class Config
{
[JsonIgnore]
public string ConfigPath { get; set; }
public string ProjectsDirectory { get; set; }
public string UserDirectory { get; set; }
[JsonIgnore]
public string ProjectsDirectory => Path.Combine(UserDirectory, "Projects");
[JsonIgnore]
public string LogsDirectory => Path.Combine(UserDirectory, "Logs");
public string DevkitArmPath { get; set; }
public string EmulatorPath { get; set; }

public void Save(ILogger log)
{
log.Log($"Saving config to '{ConfigPath}'...");
IO.WriteStringFile(ConfigPath, JsonSerializer.Serialize(this), log);
}

Expand All @@ -24,7 +29,6 @@ public static Config LoadConfig(ILogger log)
string configJson = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
if (!File.Exists(configJson))
{
log.Log($"Creating default config at '{configJson}'...");
Config defaultConfig = GetDefault(log);
defaultConfig.ValidateConfig(log);
defaultConfig.ConfigPath = configJson;
Expand Down Expand Up @@ -88,7 +92,7 @@ private static Config GetDefault(ILogger log)

return new Config
{
ProjectsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "SerialLoops"),
UserDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "SerialLoops"),
DevkitArmPath = devkitArmDir,
EmulatorPath = emulatorPath
};
Expand Down
25 changes: 25 additions & 0 deletions src/SerialLoops.Lib/Items/BackgroundMusicItem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using HaruhiChokuretsuLib.Archive.Data;
using HaruhiChokuretsuLib.Archive.Event;
using HaruhiChokuretsuLib.Audio;
using HaruhiChokuretsuLib.Util;
using System;
using System.IO;
using System.Linq;

Expand Down Expand Up @@ -33,5 +36,27 @@ public void PopulateScriptUses(Project project)
sec.Objects.Where(c => c.Command.Mnemonic == EventFile.CommandVerb.BGM_PLAY.ToString()).Select(c => (e.Name[0..^1], c))))
.Where(t => t.c.Parameters[0] == Index).ToArray();
}

public AdxWaveProvider GetAdxWaveProvider(ILogger log)
{
byte[] adxBytes = Array.Empty<byte>();
try
{
adxBytes = File.ReadAllBytes(BgmFile);
}
catch
{
if (!File.Exists(BgmFile))
{
log.LogError($"Failed to load BGM file {BgmFile}: file not found.");
}
else
{
log.LogError($"Failed to load BGM file {BgmFile}: file invalid.");
}
}

return new(new AdxDecoder(adxBytes, log));
}
}
}
60 changes: 59 additions & 1 deletion src/SerialLoops.Lib/Items/ItemDescription.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace SerialLoops.Lib.Items
using HaruhiChokuretsuLib.Archive.Event;
using System.Collections.Generic;
using System.Linq;

namespace SerialLoops.Lib.Items
{
public class ItemDescription
{
Expand Down Expand Up @@ -45,5 +49,59 @@ public enum ItemType
Voice,
}

public List<ItemDescription> GetReferencesTo(Project project)
{
List<ItemDescription> references = new();
ScenarioItem scenario = (ScenarioItem)project.Items.First(i => i.Name == "Scenario");
switch (Type)
{
case ItemType.Background:
BackgroundItem bg = (BackgroundItem)this;
return project.Items.Where(i => bg.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
case ItemType.BGM:
BackgroundMusicItem bgm = (BackgroundMusicItem)this;
return project.Items.Where(i => bgm.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
case ItemType.Character_Sprite:
CharacterSpriteItem sprite = (CharacterSpriteItem)this;
return project.Items.Where(i => sprite.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
case ItemType.Chibi:
ChibiItem chibi = (ChibiItem)this;
return project.Items.Where(i => chibi.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
case ItemType.Puzzle:
PuzzleItem puzzle = (PuzzleItem)this;
if (scenario.Scenario.Commands.Any(c => c.Verb == ScenarioCommand.ScenarioVerb.PUZZLE_PHASE && c.Parameter == puzzle.Puzzle.Index))
{
references.Add(scenario);
}
return references;
case ItemType.Script:
ScriptItem script = (ScriptItem)this;
if (scenario.Scenario.Commands.Any(c => c.Verb == ScenarioCommand.ScenarioVerb.LOAD_SCENE && c.Parameter == script.Event.Index))
{
references.Add(scenario);
}
references.AddRange(project.Items.Where(i => i.Type == ItemType.Topic && ((TopicItem)i).Topic.EventIndex == script.Event.Index));
return references;
case ItemType.Topic:
TopicItem topic = (TopicItem)this;
return project.Items.Where(i => topic.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
case ItemType.Voice:
VoicedLineItem voicedLine = (VoicedLineItem)this;
return project.Items.Where(i => voicedLine.ScriptUses.Select(s => s.ScriptName).Contains(i.Name)).ToList();
default:
return references;
}
}

public List<ItemDescription> GetReferencedBy(Project project)
{
List<ItemDescription> references = new();

switch (Type)
{
default:
return references;
}
}
}
}
27 changes: 27 additions & 0 deletions src/SerialLoops.Lib/Items/ScriptItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ public ScriptItem(EventFile evt) : base(evt.Name[0..^1], ItemType.Script)
Graph.AddVertexRange(Event.ScriptSections);
}

public Dictionary<ScriptSection, List<ScriptItemCommand>> GetScriptCommandTree(Project project)
{
Dictionary<ScriptSection, List<ScriptItemCommand>> commands = new();
foreach (ScriptSection section in Event.ScriptSections)
{
commands.Add(section, new());
foreach (ScriptCommandInvocation command in section.Objects)
{
commands[section].Add(ScriptItemCommand.FromInvocation(command, section, commands[section].Count, Event, project));
}
}
return commands;
}

public void CalculateGraphEdges(Dictionary<ScriptSection, List<ScriptItemCommand>> commandTree)
{
foreach (ScriptSection section in commandTree.Keys)
Expand All @@ -38,6 +52,10 @@ public void CalculateGraphEdges(Dictionary<ScriptSection, List<ScriptItemCommand
Event.LabelsSection.Objects.Where(l =>
Event.MapCharactersSection.Objects.Select(c => c.TalkScriptBlock).Contains(l.Id))
.Select(l => l.Name.Replace("/", "")).Contains(s.Name)).Select(s => new ScriptSectionEdge() { Source = section, Target = s }));
Graph.AddEdgeRange(Event.ScriptSections.Where(s =>
Event.LabelsSection.Objects.Where(l =>
Event.InteractableObjectsSection.Objects.Select(o => o.ScriptBlock).Contains(l.Id))
.Select(l => l.Name.Replace("/", "")).Contains(s.Name)).Select(s => new ScriptSectionEdge() { Source = section, Target = s }));
@continue = true;
}
else if (command.Verb == CommandVerb.GOTO)
Expand All @@ -53,6 +71,11 @@ public void CalculateGraphEdges(Dictionary<ScriptSection, List<ScriptItemCommand
{
Graph.AddEdgeRange(command.Parameters.Cast<ScriptSectionScriptParameter>()
.Where(p => p.Section is not null).Select(p => new ScriptSectionEdge() { Source = section, Target = p.Section }));
ScriptSection miss2Section = Event.ScriptSections.FirstOrDefault(s => s.Name == "NONEMiss2");
if (miss2Section is not null)
{
Graph.AddEdge(new() { Source = section, Target = Event.ScriptSections.First(s => s.Name == "NONEMiss2") }); // hardcode this section, even tho you can't get to it
}
}
else if (command.Verb == CommandVerb.SELECT)
{
Expand All @@ -71,6 +94,10 @@ public void CalculateGraphEdges(Dictionary<ScriptSection, List<ScriptItemCommand
{
@continue = true;
}
else if (Name.StartsWith("CHS") && Name.EndsWith("90") && commandTree.Keys.ToList().IndexOf(section) > 1 && command.Index == 0)
{
Graph.AddEdge(new() { Source = Event.ScriptSections[1], Target = section }); // these particular chess files have no VGOTOs, so uh... we manually hardcode them
}
}
if (@continue)
{
Expand Down
36 changes: 36 additions & 0 deletions src/SerialLoops.Lib/Items/VoicedLineItem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using HaruhiChokuretsuLib.Archive.Event;
using HaruhiChokuretsuLib.Audio;
using HaruhiChokuretsuLib.Util;
using System;
using System.IO;
using System.Linq;

Expand All @@ -18,6 +20,40 @@ public VoicedLineItem(string voiceFile, int index, Project project) : base(Path.
Index = index;
PopulateScriptUses(project);
}

public AdxWaveProvider GetAdxWaveProvider(ILogger log)
{
byte[] adxBytes = Array.Empty<byte>();
try
{
adxBytes = File.ReadAllBytes(VoiceFile);
}
catch
{
if (!File.Exists(VoiceFile))
{
log.LogError($"Failed to load voice file {VoiceFile}: file not found.");
}
else
{
log.LogError($"Failed to load voice file {VoiceFile}: file invalid.");
}
}

AdxType = (AdxEncoding)adxBytes[4];

IAdxDecoder decoder;
if (AdxType == AdxEncoding.Ahx10 || AdxType == AdxEncoding.Ahx11)
{
decoder = new AhxDecoder(adxBytes, log);
}
else
{
decoder = new AdxDecoder(adxBytes, log);
}

return new AdxWaveProvider(decoder);
}

public override void Refresh(Project project)
{
Expand Down
4 changes: 4 additions & 0 deletions src/SerialLoops.Lib/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,9 @@ public static Project OpenProject(string projFile, Config config, ILogger log, I
project.LoadArchives(log, tracker);
return project;
}
public List<ItemDescription> GetSearchResults(string searchTerm)
{
return Items.Where(item => item.Name.Contains(searchTerm.Trim(), StringComparison.OrdinalIgnoreCase) || item.DisplayName.Contains(searchTerm.Trim(), StringComparison.OrdinalIgnoreCase)).ToList();
}
}
}
11 changes: 6 additions & 5 deletions src/SerialLoops.Lib/RecentProjects.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using HaruhiChokuretsuLib.Util;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using HaruhiChokuretsuLib.Util;
using System.Text.Json.Serialization;

namespace SerialLoops.Lib
{
public class RecentProjects
{
private const int MAX_RECENT_PROJECTS = 10;
private const int MAX_RECENT_PROJECTS = 10;
[JsonIgnore]
public string RecentProjectsPath { get; set; }
public List<string> Projects { get; set; }

Expand All @@ -18,9 +19,9 @@ public void Save(ILogger log)
IO.WriteStringFile(RecentProjectsPath, JsonSerializer.Serialize(this), log);
}

public static RecentProjects LoadRecentProjects(ILogger log)
public static RecentProjects LoadRecentProjects(Config config, ILogger log)
{
string recentProjectsJson = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "recent_projects.json");
string recentProjectsJson = Path.Combine(config.UserDirectory, "recent_projects.json");
if (!File.Exists(recentProjectsJson))
{
log.Log($"Creating default recent projects cache at '{recentProjectsJson}'...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ public List<ScriptItemCommand> WalkCommandGraph(Dictionary<ScriptSection, List<S
{
dfs.Compute(commandTree.Keys.First());
}
bool test = observer.TryGetPath(Section, out IEnumerable<ScriptSectionEdge> path);
bool success = observer.TryGetPath(Section, out IEnumerable<ScriptSectionEdge> path);

if (!success)
{
return null;
}

foreach (ScriptSectionEdge edge in path)
{
Expand Down Expand Up @@ -572,7 +577,7 @@ private static List<ScriptParameter> GetScriptParameters(ScriptCommandInvocation
parameters.Add(new BgScriptParameter("Background", (BackgroundItem)project.Items.First(i => i.Type == ItemDescription.ItemType.Background && ((BackgroundItem)i).Id == parameter), kinetic: false));
break;
case 1:
parameters.Add(new ShortScriptParameter("Unknown", parameter));
parameters.Add(new BoolScriptParameter("Display from Bottom", parameter == 1));
break;
}
break;
Expand Down
2 changes: 1 addition & 1 deletion src/SerialLoops.Lib/SerialLoops.Lib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HaruhiChokuretsuLib" Version="0.16.2" />
<PackageReference Include="HaruhiChokuretsuLib" Version="0.16.4" />
<PackageReference Include="NitroPacker.Core" Version="2.0.1" />
<PackageReference Include="QuikGraph" Version="2.5.0" />
</ItemGroup>
Expand Down
Loading