Skip to content
Draft
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
105 changes: 78 additions & 27 deletions OpenKh.Bbs/Scd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class StreamHeader
[Data] public uint StreamSize { get; set; }
[Data] public uint ChannelCount { get; set; }
[Data] public uint SampleRate { get; set; }
[Data] public uint Codec { get; set; }
[Data] public uint Codec { get; set; } //6 = .ogg, 12 = MSADPCM .wav
[Data] public uint LoopStart { get; set; }
[Data] public uint LoopEnd { get; set; }
[Data] public uint ExtraDataSize { get; set; }
Expand All @@ -63,44 +63,95 @@ public class VAGHeader
[Data(Count = 16)] public string Name { get; set; }
}

public static Header header = new Header();
public static TableOffsetHeader tableOffsetHeader = new TableOffsetHeader();
public static List<byte[]> StreamFiles = new List<byte[]>();
public Header FileHeader = new();
public TableOffsetHeader tableOffsetHeader = new();
public List<StreamHeader> StreamHeaders = [];
public List<byte[]> StreamFiles = [];
public List<byte[]> MediaFiles = []; //6 = .ogg file, everything else is a .wav with msadpcm codec, throw it at ffmpeg /shrug

public static Scd Read(Stream stream)
{
Scd scd = new Scd();

header = BinaryMapping.ReadObject<Header>(stream);
tableOffsetHeader = BinaryMapping.ReadObject<TableOffsetHeader>(stream);
stream.Seek(0, SeekOrigin.Begin);

var scd = new Scd
{
FileHeader = BinaryMapping.ReadObject<Header>(stream),
tableOffsetHeader = BinaryMapping.ReadObject<TableOffsetHeader>(stream),
};

stream.Seek(tableOffsetHeader.Table1Offset, SeekOrigin.Begin);
stream.Seek(scd.tableOffsetHeader.Table1Offset, SeekOrigin.Begin);

List<uint> SoundOffsets = new List<uint>();
for (int i = 0; i < tableOffsetHeader.Table1ElementCount; i++)
{
SoundOffsets.Add(stream.ReadUInt32());
}
var soundOffsets = new List<uint>();
for (var i = 0; i < scd.tableOffsetHeader.Table1ElementCount; i++) soundOffsets.Add(stream.ReadUInt32());

foreach(uint off in SoundOffsets)
for (var index = 0; index < soundOffsets.Count; index++)
{
var off = soundOffsets[index];
var next = (int)(index == soundOffsets.Count - 1 ? stream.Length : soundOffsets[index + 1] - off);
stream.Seek(off, SeekOrigin.Begin);
var streamInfo = BinaryMapping.ReadObject<StreamHeader>(stream);

byte[] st = stream.ReadBytes((int)streamInfo.StreamSize);
StreamFiles.Add(st);

// Convert to VAG Streams.
/*VAGHeader vagHeader = new VAGHeader();
vagHeader.ChannelCount = (byte)streamInfo.ChannelCount;
vagHeader.SamplingFrequency = (byte)streamInfo.SampleRate;
vagHeader.Version = 3;
vagHeader.Magic = 0x70474156;
vagHeader.Name = p.ToString();*/
scd.StreamHeaders.Add(streamInfo);

var st = stream.ReadBytes(next - 0x20);
scd.StreamFiles.Add(st);

//https://github.com/Leinxad/KHPCSoundTools/blob/main/SCDInfo/Program.cs#L109
switch (streamInfo.Codec)
{
case 6:
{
var extradataOffset = 0u;
if (streamInfo.AuxChunkCount > 0) extradataOffset += BitConverter.ToUInt32(st.Skip(0x04).Take(4).ToArray(), 0);

var encryptionKey = st[extradataOffset + 0x02];
var seekTableSize = BitConverter.ToUInt32(st.Skip((int)extradataOffset + 0x10).Take(4).ToArray(), 0);
var vorbHeaderSize = BitConverter.ToUInt32(st.Skip((int)extradataOffset + 0x14).Take(4).ToArray(), 0);

var startOffset = extradataOffset + 0x20 + seekTableSize;

var decryptedFile = st.ToArray();

var endPosition = startOffset + vorbHeaderSize;

for (var i = startOffset; i < endPosition; i++) decryptedFile[i] = (byte)(decryptedFile[i]^encryptionKey);

var oggSize = vorbHeaderSize + streamInfo.StreamSize;

scd.MediaFiles.Add(decryptedFile.Skip((int)startOffset).Take((int)oggSize).ToArray());
break;
}
case 12:
{
var streamSize = streamInfo.StreamSize;
var channelCount = streamInfo.ChannelCount;
var sampleRate = streamInfo.SampleRate;

var length = streamSize + (0x4e - 0x8);

var msadpcm = Array.Empty<byte>()
.Concat(BitConverter.GetBytes(0x46464952)) //"RIFF"
.Concat(BitConverter.GetBytes(length)) //overall file size - 8
.Concat(BitConverter.GetBytes(0x45564157)) //"WAVE"
.Concat(BitConverter.GetBytes(0x20746D66)) //"fmt "
.Concat(BitConverter.GetBytes(0x32))
.Concat(st.Take(0x32))
.Concat(BitConverter.GetBytes(0x61746164)) //"data"
.Concat(BitConverter.GetBytes((int)streamSize))
.Concat(st.Skip(0x32))
.ToArray();

scd.MediaFiles.Add(msadpcm);
break;
}
default:
{
scd.MediaFiles.Add(null);
break;
}
}
}



return scd;
}

Expand Down
20 changes: 17 additions & 3 deletions OpenKh.Egs/EgsTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ public class EgsTools

#region Extract

public static void Extract(string inputHed, string output, bool doNotExtractAgain = false)
public enum ExtractFileOutputMode
{
SeparateRoot,
Adjacent,
}

public static void Extract(string inputHed, string output, bool doNotExtractAgain = false, ExtractFileOutputMode outputMode = ExtractFileOutputMode.SeparateRoot)
{
var outputDir = output ?? Path.GetFileNameWithoutExtension(inputHed);
using var hedStream = File.OpenRead(inputHed);
Expand All @@ -81,7 +87,11 @@ public static void Extract(string inputHed, string output, bool doNotExtractAgai
if (!Names.TryGetValue(hash, out var fileName))
fileName = $"{hash}.dat";

var outputFileName = Path.Combine(outputDir, ORIGINAL_FILES_FOLDER_NAME, fileName);
var outputFileName = outputMode switch
{
ExtractFileOutputMode.Adjacent => Path.Combine(outputDir, fileName),
_ => Path.Combine(outputDir, ORIGINAL_FILES_FOLDER_NAME, fileName),
};

if (doNotExtractAgain && File.Exists(outputFileName))
continue;
Expand All @@ -93,7 +103,11 @@ public static void Extract(string inputHed, string output, bool doNotExtractAgai

File.Create(outputFileName).Using(stream => stream.Write(hdAsset.OriginalData));

outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName);
outputFileName = outputMode switch
{
ExtractFileOutputMode.Adjacent => Path.Combine(outputDir, $"{fileName}.remastered.d"),
_ => Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName),
};

foreach (var asset in hdAsset.Assets)
{
Expand Down
2 changes: 2 additions & 0 deletions OpenKh.Godot/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
20 changes: 20 additions & 0 deletions OpenKh.Godot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Godot 4+ specific ignores
.godot/
/android/

# Jetbrains
.idea/

# Kingdom Hearts extracted and imported assets

Extracted/

Imported/**/*.mdlx
Imported/**/*.mdls
Imported/**/*.mset
Imported/**/*.dds
Imported/**/*.scd
Imported/**/*.map
Imported/**/*.png
Imported/**/*.2dd
Imported/**/*.2ld
7 changes: 7 additions & 0 deletions OpenKh.Godot/Animation/IBoneCollisionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OpenKh.Godot.Animation
{
public interface IBoneCollisionProvider
{

}
}
16 changes: 16 additions & 0 deletions OpenKh.Godot/Animation/InterpolatedAnimation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Godot;

namespace OpenKh.Godot.Animation
{
public partial class InterpolatedAnimation : Resource
{
[Export] public float TargetFPS; //within this class, this does nothing. this is intended for eventually serializing back to an anb
[Export] public int BoneCount;
[Export] public int AdditionalBoneCount;
[Export] public float TimeStart;
[Export] public float TimeEnd;
[Export] public float TimeReturn;

//TODO
}
}
9 changes: 9 additions & 0 deletions OpenKh.Godot/Animation/InterpolatedAnimationCurve.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Godot;

namespace OpenKh.Godot.Animation
{
public partial class InterpolatedAnimationCurve : Resource
{
//TODO
}
}
15 changes: 15 additions & 0 deletions OpenKh.Godot/Animation/KH2Bone.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Numerics;

namespace OpenKh.Godot.Animation
{
public struct KH2Bone
{
public int Sibling;
public int Parent;
public int Child;
public int Flags;
public Vector4 Scale;
public Vector4 Rotation;
public Vector4 Position;
}
}
31 changes: 31 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2AnimatedShader.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
shader_type spatial;
render_mode unshaded;

#include "res://Assets/Shaders/KH2TextureAnimationCommon.gdshaderinc"

void fragment()
{
if (Scissor)
{
ALPHA_SCISSOR_THRESHOLD = 0.5;
}
vec4 sample;
if (
TextureAnimationOverlay(UseSprite0, UV, TextureOriginalUV0, TextureFrame0, Sprite0, sample) ||
TextureAnimationOverlay(UseSprite1, UV, TextureOriginalUV1, TextureFrame1, Sprite1, sample) ||
TextureAnimationOverlay(UseSprite2, UV, TextureOriginalUV2, TextureFrame2, Sprite2, sample) ||
TextureAnimationOverlay(UseSprite3, UV, TextureOriginalUV3, TextureFrame3, Sprite3, sample)
)
{

}
else
{
sample = texture(Texture, UV);
}
ALBEDO = sample.rgb;
if (Scissor || Alpha)
{
ALPHA = sample.a;
}
}
18 changes: 18 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2BasicShader.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
shader_type spatial;
render_mode unshaded;

#include "res://Assets/Shaders/KH2Common.gdshaderinc"

void fragment()
{
if (Scissor)
{
ALPHA_SCISSOR_THRESHOLD = 0.5;
}
vec4 sample = texture(Texture, UV);
ALBEDO = sample.rgb;
if (Scissor || Alpha)
{
ALPHA = sample.a;
}
}
3 changes: 3 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2Common.gdshaderinc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
uniform sampler2D Texture : hint_default_white, source_color;
uniform bool Scissor;
uniform bool Alpha;
16 changes: 16 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2InterfaceAddShader.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
shader_type canvas_item;
render_mode unshaded, blend_add;

#include "KH2InterfaceCommon.gdshaderinc"

void vertex()
{
vertColor = COLOR;
minimaxi = CUSTOM0;
uvAdj = CUSTOM1.rg;
}

void fragment()
{
COLOR = Compute(vertColor, UV, TEXTURE);
}
16 changes: 16 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2InterfaceCommon.gdshaderinc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
varying vec4 vertColor;
varying vec4 minimaxi;
varying vec2 uvAdj;

vec2 wrap(vec2 value, vec2 minimum, vec2 maximum)
{
vec2 s = maximum - minimum;
return minimum + mod(mod(value - minimum, s) + s, s);
}
vec4 Compute(vec4 color, vec2 uv, sampler2D tex)
{
vec2 uvAdjusted = wrap((uv + (uvAdj * TIME)), minimaxi.xy, minimaxi.zw);
vec2 finalUv = vec2(uvAdj.x != 0.0 ? uvAdjusted.x : uv.x, uvAdj.y != 0.0 ? uvAdjusted.y : uv.y);
return texture(tex, finalUv) * color;
//return texture(tex, uv) * color;
}
16 changes: 16 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2InterfaceMixShader.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
shader_type canvas_item;
render_mode unshaded, blend_mix;

#include "KH2InterfaceCommon.gdshaderinc"

void vertex()
{
vertColor = COLOR;
minimaxi = CUSTOM0;
uvAdj = CUSTOM1.rg;
}

void fragment()
{
COLOR = Compute(vertColor, UV, TEXTURE);
}
16 changes: 16 additions & 0 deletions OpenKh.Godot/Assets/Shaders/KH2InterfaceSubShader.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
shader_type canvas_item;
render_mode unshaded, blend_sub;

#include "KH2InterfaceCommon.gdshaderinc"

void vertex()
{
vertColor = COLOR;
minimaxi = CUSTOM0;
uvAdj = CUSTOM1.rg;
}

void fragment()
{
COLOR = Compute(vertColor, UV, TEXTURE);
}
Loading