Commit d579374c authored by PoroCYon's avatar PoroCYon

Add rudimentary support for detached agrp files (audiogroup1.dat etc)

This was made in quite a hurry, code's currently a mess and should be
cleaned up soon(tm).

Basically, it's another FORM, with a single chunk, AUDO, containing a
count-offset list of {uint length; byte WAV_data[length];} pairs.

Actually, it *might* work using the GMFile exporter, but I don't know
(but maybe I should've tested this before writing this crap).
parent 26c240a1
Pipeline #35008884 passed with stages
in 1 minute and 57 seconds
......@@ -167,7 +167,7 @@ namespace Altar
VariableExtra = new uint [0];
ChunkOrder = new SectionHeaders [0];
}
static T[] TryReadMany<T>(SectionCountOffsets* hdr, Func<uint, T> readOne)
internal static T[] TryReadMany<T>(SectionCountOffsets* hdr, Func<uint, T> readOne)
{
if (hdr == null || hdr->Header.IsEmpty()) return ArrayExt<T>.Empty;
......@@ -430,6 +430,72 @@ namespace Altar
[DebuggerStepThrough]
public static bool IsEmpty( SectionHeader header) => header. Size <= 4;
}
public unsafe class AGRPFile : IDisposable
{
public AGRPFileContent Content{get;private set;}
public uint NameOffset{get;internal set;}
public byte[][] Waves{get;internal set;}
internal AGRPFile() {
Waves=new byte[0][];
}
internal AGRPFile(AGRPFileContent f) {
Content = f;
Waves=GMFile.TryReadMany(f.Audo, i=>SectionReader.GetAgrpAudo(f, i));
}
public void Dispose()
{
if (Content != null)
{
Content.Dispose();
Content = null;
}
}
public static AGRPFile GetFile(byte[] data) {
var ret =new AGRPFileContent();
var hdr_bp=new UniquePtr(data);
byte* hdr_b=hdr_bp.BPtr;
var basePtr= (SectionHeader*)hdr_b;
ret.Form=basePtr;
if (ret.Form->Identity!=SectionHeaders.Form)
throw new InvalidDataException(ERR_NO_FORM);
SectionHeader*
hdr=basePtr+1,
hdrEnd=(SectionHeader*)((IntPtr)basePtr+(int)ret.Form->Size);
int headersMet=0;
while(hdr<hdrEnd) {
switch(hdr->Identity) {
case SectionHeaders.Audio:
ret.Audo=(SectionCountOffsets*)hdr;
break;
default:
Console.WriteLine("Unexpected chunk in audiogroup.dat: "
+ hdr->Identity.ToChunkName());
break;
}
headersMet++;
hdr=unchecked((SectionHeader*)((IntPtr)hdr+(int)hdr->Size)+1);
}
ret.RawData=hdr_bp;
return new AGRPFile(ret);
}
public static AGRPFile GetFile(string path) => GetFile(File.ReadAllBytes(path));
[DebuggerStepThrough]
public static void* PtrFromOffset(AGRPFileContent file, long offset) => file.RawData.BPtr + offset;
}
public static class GMFileExt
{
[DebuggerStepThrough]
......
......@@ -228,5 +228,34 @@ namespace Altar
Disposing();
}
}
public unsafe class AGRPFileContent : IDisposable
{
public UniquePtr RawData;
public SectionHeader* Form;
public SectionCountOffsets* Audo; // not the same as a data.win AGRP!
void Disposing()
{
if (RawData != null)
{
RawData.Dispose();
RawData = null;
}
}
public void Dispose()
{
Disposing();
GC.SuppressFinalize(this);
}
~AGRPFileContent()
{
Disposing();
}
}
}
......@@ -3,6 +3,13 @@ using CommandLine.Text;
namespace Altar
{
public class ExportAgrpOptions
{
[Option('f', "file", Required=true,DefaultValue="audiogroup1.dat",HelpText="Input file (default: 'audiogroup1.dat')")]
public string File{get;set;}
[Option('o',"out",Required=true,DefaultValue=".",HelpText="Output directory (default: current)")]
public string OutputDirectory{get;set;}
}
public class ExportOptions
{
// free short flags: lvxyz
......@@ -221,6 +228,9 @@ namespace Altar
set;
}
[VerbOption("export-agrp", HelpText="Export contents of an audio group file (eg. audiogroup1.dat).")]
public ExportAgrpOptions ExportAgrp{get;set;}
[HelpVerbOption]
public string GetUsage(string v) => HelpText.AutoBuild(this, v);
}
......
......@@ -21,13 +21,14 @@ namespace Altar
unsafe static class Program
{
static bool quiet, nopp;
static ExportOptions eos;
static void Write (string s) { if (!eos.Quiet) Console.Write (s); }
static void WriteLine(string s) { if (!eos.Quiet) Console.WriteLine(s); }
static void GetCurPos(out int l, out int t)
{
if (eos.NoPrecProg)
if (nopp)
{
l = t = 0;
return;
......@@ -38,12 +39,12 @@ namespace Altar
}
static void SetCurPos(int l, int t)
{
if (!eos.NoPrecProg)
if (!nopp)
Console.SetCursorPosition(l, t);
}
static void SetCAndWr(int l, int t, string s)
{
if (!eos.Quiet && !eos.NoPrecProg)
if (!quiet && !nopp)
{
Console.SetCursorPosition(l, t);
Console.Write(s);
......@@ -51,11 +52,11 @@ namespace Altar
}
static void WrAndGetC(string s, out int l, out int t)
{
if (!eos.Quiet)
if (!quiet)
{
Console.Write(s);
if (eos.NoPrecProg)
if (nopp)
{
l = t = 0;
return;
......@@ -125,6 +126,8 @@ namespace Altar
#endregion
eos = eo;
quiet=eo.Quiet;
nopp=eo.NoPrecProg;
// ---
#region GEN8
......@@ -753,6 +756,25 @@ namespace Altar
return writer.Buffer.ReadBytes(writer.Buffer.Size);
}
static void ExportAgrp(ExportAgrpOptions opt) {
var file=Path.GetFullPath(opt.File);
var outd=Path.GetFullPath(opt.OutputDirectory);
if (!Directory.Exists(outd))Directory.CreateDirectory(outd);
if (!File.Exists(file))throw new ParserException("File \"" + file + "\" doesn't exist.");
nopp=quiet=false;
using (var f=AGRPFile.GetFile(file)){
int cl = 0, ct = 0;
WrAndGetC("Exporting AGRP audio... ", out cl, out ct);
for (int i =0; i<f.Waves.Length;++i){
SetCAndWr(cl, ct, O_PAREN + (i + 1) + SLASH + f.Waves.Length + C_PAREN);
File.WriteAllBytes("audo" + i.ToString("D3") + ".wav", f.Waves[i]);
}
}
Console.WriteLine();
}
[STAThread]
static void Main(string[] args)
{
......@@ -775,6 +797,9 @@ namespace Altar
case "import":
Import((ImportOptions)vo);
break;
case "export-agrp":
ExportAgrp((ExportAgrpOptions)vo);
break;
}
}
catch (Exception)
......
......@@ -32,7 +32,7 @@ namespace Altar.Unpack
return (long)++chunk + 0x4 - (long)png;
}
internal static string ReadString(byte* ptr)
{
var length = *(UInt32*)ptr;
......@@ -676,6 +676,18 @@ namespace Altar.Unpack
return StringFromOffset(content, *(uint*)ag); // it's just a name
}
public static byte[] GetAgrpAudo(AGRPFileContent c,uint id){
if (id >= c.Audo->Count)
throw new ArgumentOutOfRangeException(nameof(id));
var au=(AudioEntry*)AGRPFile.PtrFromOffset(c,(&c.Audo->Offsets)[id]);
byte[] ret = new byte[au->Length];
Marshal.Copy((IntPtr)(&au->Data), ret, 0, ret.Length);
return ret;
}
public static byte[][] ListToByteArrays(GMFileContent content, SectionCountOffsets* list, long elemLen = 0)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment