Skip to content

Commit

Permalink
Merge pull request #107 from Xeeynamo/feature/bar-editor-improvements
Browse files Browse the repository at this point in the history
BAR editor improvements
  • Loading branch information
Vladabdf authored May 24, 2020
2 parents ae4cb46 + e91e1ec commit 08c9f63
Show file tree
Hide file tree
Showing 24 changed files with 938 additions and 293 deletions.
116 changes: 116 additions & 0 deletions OpenKh.Command.Bar/Core.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace OpenKh.Command.Bar
{
public static class Core
{
internal class BarRoot
{
[JsonProperty] public string OriginalFileName { get; set; }

[JsonProperty] public List<BarDesc> Entries { get; set; }
}

internal class BarDesc
{
[JsonProperty] public string FileName { get; set; }
[JsonProperty] public string InternalName { get; set; }
[JsonProperty] public int TypeId { get; set; }
[JsonProperty] public int LinkIndex { get; set; }
[JsonIgnore] public Stream Stream { get; set; }

public override string ToString() => FileName;
}

private const string InvalidBarText = "The specified file is not a BAR file.";
internal static readonly Exception InvalidBarFileException = new InvalidDataException(InvalidBarText);

public static List<Kh2.Bar.Entry> ReadEntries(string fileName)
{
using var stream = File.OpenRead(fileName);
if (!Kh2.Bar.IsValid(stream))
throw InvalidBarFileException;

stream.Position = 0;
return Kh2.Bar.Read(stream);
}

public static void ExportProject(string inputFileName, string outputFolder, bool suppress = false)
{
var barEntries = ReadEntries(inputFileName);
var project = new BarRoot
{
OriginalFileName = Path.GetFileName(inputFileName),
Entries = barEntries
.Select(x => new BarDesc
{
FileName = $"{x.Name}.{Helpers.GetSuggestedExtension(x.Type)}",
InternalName = x.Name,
TypeId = (int)x.Type,
LinkIndex = x.Index,
Stream = x.Stream,
})
.ToList()
};

foreach (var entryGroup in project.Entries
.Where(x => x.LinkIndex == 0)
.GroupBy(x => $"{x.InternalName}_{x.TypeId}"))
{
var items = entryGroup.ToArray();
if (items.Length > 1)
{
for (var i = 0; i < items.Length; i++)
{
var ext = Helpers.GetSuggestedExtension((Kh2.Bar.EntryType)items[0].TypeId);
items[i].FileName = $"{items[0].InternalName}_{i}.{ext}";
}
}
}

if (!suppress)
{
var fileNameWithExt = Path.GetFileName(inputFileName);
var projectFileName = Path.Combine(outputFolder, $"{fileNameWithExt}.json");
if (!Directory.Exists(outputFolder))
Directory.CreateDirectory(outputFolder);

File.WriteAllText(projectFileName, JsonConvert.SerializeObject(project, Formatting.Indented));
}

foreach (var entry in project.Entries.Where(x => x.LinkIndex == 0))
{
var outputFileName = Path.Combine(outputFolder, entry.FileName);

using var stream = File.Create(outputFileName);
entry.Stream.Position = 0;
entry.Stream.CopyTo(stream);
}
}

public static IEnumerable<Kh2.Bar.Entry> ImportProject(string inputProjectName, out string originalFileName)
{
var baseDirectory = Path.GetDirectoryName(inputProjectName);
var project = JsonConvert.DeserializeObject<BarRoot>(File.ReadAllText(inputProjectName));
originalFileName = project.OriginalFileName;

var streams = project.Entries
.Where(x => x.LinkIndex == 0)
.ToDictionary(x => x.FileName,
x => File.OpenRead(Path.Combine(baseDirectory, x.FileName)));

return project.Entries
.Select(x => new Kh2.Bar.Entry
{
Name = x.InternalName,
Type = (Kh2.Bar.EntryType)x.TypeId,
Index = x.LinkIndex,
Stream = x.LinkIndex == 0 ? streams[x.FileName] : null
});
}
}
}
65 changes: 65 additions & 0 deletions OpenKh.Command.Bar/Helpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using static OpenKh.Kh2.Bar;
using System.Collections.Generic;

namespace OpenKh.Command.Bar
{
public class Helpers
{
private static readonly string DefaultExtension = "bin";

private static readonly Dictionary<EntryType, string> SuggestedExtensions =
new Dictionary<EntryType, string>
{
[EntryType.Dummy] = "dummy",
[EntryType.Binary] = "bin",
[EntryType.List] = "list",
[EntryType.Ai] = "ai",
[EntryType.Model] = "model",
[EntryType.MeshOcclusion] = "doct",
[EntryType.MapCollision] = "coct",
[EntryType.ModelTexture] = "modeltexture",
[EntryType.Dpx] = "dpx",
[EntryType.AnimationData] = "animation",
[EntryType.Texture] = "texture",
[EntryType.CameraCollision] = "coctcamera",
[EntryType.SpawnPoint] = "spawnpoint",
[EntryType.SpawnScript] = "spawnscript",
[EntryType.MapColorDiffuse] = "colordiffuse",
[EntryType.LightData] = "coctlight",
[EntryType.Anb] = "Anb",
[EntryType.Bar] = "bar",
[EntryType.Pax] = "pax",
[EntryType.MapCollision2] = "coctmap",
[EntryType.AnimationLimit] = "animlimit",
[EntryType.Unknown21] = "unk21",
[EntryType.AnimationLoader] = "animload",
[EntryType.ModelCollision] = "coctmodel",
[EntryType.Imgd] = "imd",
[EntryType.Seqd] = "sequence",
[EntryType.Layout] = "layout",
[EntryType.Imgz] = "imz",
[EntryType.AnimationMap] = "mapanim",
[EntryType.Seb] = "seb",
[EntryType.Wd] = "wd",
[EntryType.Unknown33] = "unk33",
[EntryType.IopVoice] = "iopvoice",
[EntryType.RawBitmap] = "rgb",
[EntryType.MemoryCard] = "memcard",
[EntryType.WrappedCollisionData] = "coctwrapped",
[EntryType.Unknown39] = "unk39",
[EntryType.Unknown40] = "unk40",
[EntryType.Unknown41] = "unk41",
[EntryType.Minigame] = "minigame",
[EntryType.JimiData] = "jimidata",
[EntryType.Progress] = "progress",
[EntryType.Synthesis] = "synthesis",
[EntryType.BarUnknown] = "bar",
[EntryType.Vibration] = "vibration",
[EntryType.Vag] = "vag",

};

public static string GetSuggestedExtension(EntryType type) =>
SuggestedExtensions.TryGetValue(type, out var ext) ? ext : DefaultExtension;
}
}
17 changes: 17 additions & 0 deletions OpenKh.Command.Bar/OpenKh.Command.Bar.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OpenKh.Kh2\OpenKh.Kh2.csproj" />
</ItemGroup>

</Project>
142 changes: 142 additions & 0 deletions OpenKh.Command.Bar/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using McMaster.Extensions.CommandLineUtils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Reflection;

namespace OpenKh.Command.Bar
{
[Command("OpenKh.Command.Bar")]
[VersionOptionFromMember("--version", MemberName = nameof(GetVersion))]
[Subcommand(
typeof(UnpackCommand),
typeof(PackCommand),
typeof(ListCommand))]
class Program
{
private const string InputProjectDesc = "BAR project file (eg. P_EX100.json).";
private const string InputBarDesc = "Kingdom Hearts II BAR file.";
private const string OutputBarDesc = "Name of the BAR file that will be created.";
private const string OutputDirDesc = "Path where the content will be extracted.";
private const string SuppressProjectCreationDesc = "Do not generate a project when unpacking.";

static int Main(string[] args)
{
try
{
return CommandLineApplication.Execute<Program>(args);
}
catch (FileNotFoundException e)
{
Console.WriteLine($"The file {e.FileName} cannot be found. The program will now exit.");
return 2;
}
catch (Exception e)
{
Console.WriteLine($"FATAL ERROR: {e.Message}\n{e.StackTrace}");
return -1;
}
}

protected int OnExecute(CommandLineApplication app)
{
app.ShowHelp();
return 1;
}

private static string GetVersion()
=> typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;

private static List<Kh2.Bar.Entry> ReadEntries(string fileName)
{
using var stream = File.OpenRead(fileName);
if (!Kh2.Bar.IsValid(stream))
throw Core.InvalidBarFileException;

stream.Position = 0;
return Kh2.Bar.Read(stream);
}

[Command(Description = "Unpack the content of a BAR file and generate a project")]
private class UnpackCommand
{
[Required]
[FileExists]
[Argument(0, Description = InputBarDesc)]
public string InputBar { get; set; }

[Option(CommandOptionType.SingleValue, Description = OutputDirDesc, ShortName = "o", LongName = "output")]
public string OutputDir { get; set; }

[Option(CommandOptionType.NoValue, Description = SuppressProjectCreationDesc, ShortName = "s", LongName = "skip")]
public bool SuppressProjectCreation { get; set; }

protected int OnExecute(CommandLineApplication app)
{
if (string.IsNullOrEmpty(OutputDir))
{
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(InputBar);
OutputDir = Path.Combine(Path.GetDirectoryName(InputBar), fileNameWithoutExt);
}

Core.ExportProject(InputBar, OutputDir, SuppressProjectCreation);

return 0;
}
}

[Command(Description = "Repack a BAR from its project file")]
private class PackCommand
{
[Required]
[FileExists]
[Argument(0, Description = InputProjectDesc)]
public string InputProject { get; set; }

[Option(CommandOptionType.SingleValue, Description = OutputBarDesc, ShortName = "o", LongName = "output")]
public string OutputFile { get; set; }

protected int OnExecute(CommandLineApplication app)
{
var baseDirectory = Path.GetDirectoryName(InputProject);
var bar = Core.ImportProject(InputProject, out var originalFileName);

if (string.IsNullOrEmpty(OutputFile))
OutputFile = Path.Combine(baseDirectory, originalFileName);

if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) &&
File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory))
OutputFile = Path.Combine(OutputFile, originalFileName);

using var outputStream = File.Create(OutputFile);
Kh2.Bar.Write(outputStream, bar);

foreach (var entry in bar)
entry.Stream?.Dispose();

return 0;
}
}

[Command(Description = "Print content of a BAR file")]
private class ListCommand
{
[Required]
[FileExists]
[Argument(0, Description = InputBarDesc)]
public string InputBar { get; set; }

protected int OnExecute(CommandLineApplication app)
{
using var stream = File.OpenRead(InputBar);
foreach (var entry in Kh2.Bar.Read(stream))
Console.WriteLine($"{entry.Name}, {entry.Type}, {entry.Index}");

return 0;
}
}
}
}
Loading

0 comments on commit 08c9f63

Please sign in to comment.