From b5a2055da9b2f8aec2336c55233b4aea4e706d43 Mon Sep 17 00:00:00 2001 From: ficedula Date: Fri, 6 Oct 2023 15:41:53 +0100 Subject: [PATCH] Add initial camdat decoders --- Ficedula.FF7/Battle/Camera.cs | 245 ++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 Ficedula.FF7/Battle/Camera.cs diff --git a/Ficedula.FF7/Battle/Camera.cs b/Ficedula.FF7/Battle/Camera.cs new file mode 100644 index 0000000..da6d921 --- /dev/null +++ b/Ficedula.FF7/Battle/Camera.cs @@ -0,0 +1,245 @@ +// This program and the accompanying materials are made available under the terms of the +// Eclipse Public License v2.0 which accompanies this distribution, and is available at +// https://www.eclipse.org/legal/epl-v20.html +// +// SPDX-License-Identifier: EPL-2.0 + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Ficedula.FF7.Battle { + public class CameraData : IDisposable { + + private Stream _source; + + private List _offsets; + public int CameraCount { get; private set; } + + public CameraData(Stream source) { + _source = source; + + var offsets = Enumerable.Range(0, 4) + .Select(_ => (int)(source.ReadU32() - 0x801A0000)) + .ToArray(); + CameraCount = (offsets[1] - offsets[0]) / 12; //4 byte pointer, 3 cameras per entry + + _offsets = Enumerable.Range(0, 4) + .Select(i => { + int count = i < 2 ? CameraCount : 1; + source.Position = offsets[i]; + int[] pointers = Enumerable.Range(0, count) + .Select(_ => (int)(source.ReadU32() - 0x801A0000)) + .ToArray(); + return pointers; + }) + .ToList(); + } + + private byte[] Read(int camera, int index, bool focus, bool victoryCamera) { + int[] offsets = _offsets[(focus ? 1 : 0) + (victoryCamera ? 2 : 0)]; + int offset = offsets[camera * 3 + index], + next = offsets[camera * 3 + index + 1]; + byte[] data = new byte[next - offset]; + _source.Position = offset; + _source.Read(data, 0, data.Length); + return data; + } + + public CameraPositionScript ReadPosition(int camera, int index, bool isVictory) { + return new CameraPositionScript(Read(camera, index, false, isVictory)); + } + public CameraFocusScript ReadFocus(int camera, int index, bool isVictory) { + return new CameraFocusScript(Read(camera, index, true, isVictory)); + } + + + public void Dispose() { + _source.Dispose(); + } + } + + public abstract class CameraScript where T : struct { + + protected static Dictionary _opParams; + protected static T _conditionalRestartOp; + + private byte[] _bytecode; + private int _ip; + + protected CameraScript(byte[] bytecode) { + _bytecode = bytecode; + } + + protected abstract T FromByte(byte b); + + public DecodedCameraOp NextOp() { + var op = new DecodedCameraOp { + Opcode = FromByte(_bytecode[_ip++]), + }; + if (_opParams.TryGetValue(op.Opcode, out int[] parms)) { + op.Operands = parms + .Select(i => { + switch (i) { + case 1: + return (int)_bytecode[_ip++]; + case 2: + short s = BitConverter.ToInt16(_bytecode, _ip); + _ip += 2; + return s; + default: + throw new NotImplementedException(); + } + }) + .ToArray(); + } + return op; + } + } + + public class CameraPositionScript : CameraScript { + + static CameraPositionScript() { + _opParams = new() { + [CameraPositionOpcode.UnknownD5] = new[] { 2 }, + [CameraPositionOpcode.UnknownD7] = new[] { 1, 1 }, + [CameraPositionOpcode.UnknownD8] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.SetActiveIdleCamera] = new[] { 1 }, + [CameraPositionOpcode.UnknownDE] = new[] { 1 }, + [CameraPositionOpcode.UnknownE0] = new[] { 1, 1 }, + [CameraPositionOpcode.UnknownE2] = new[] { 1 }, + [CameraPositionOpcode.UnknownE3] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.TransitionToAttackerJoint] = new[] { 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.TransitionToTargetJoint] = new[] { 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.UnknownE6] = new[] { 2, 2, 2, 1 }, + [CameraPositionOpcode.UnknownE7] = new[] { 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.UnknownE9] = new[] { 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.UnknownEB] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraPositionOpcode.UnknownEF] = new[] { 1, 1, 2, 2, 2 }, + [CameraPositionOpcode.UnknownF0] = new[] { 1, 2, 2, 2 }, + [CameraPositionOpcode.UnknownF2] = new[] { 1, 2, 2 }, + [CameraPositionOpcode.UnknownF3] = new[] { 1, 2, 2 }, + [CameraPositionOpcode.SetWait] = new[] { 1 }, + [CameraPositionOpcode.UnknownF7] = new[] { 1, 2, 2, 2 }, + [CameraPositionOpcode.UnknownF8] = new[] { 2, 2, 2, 2, 2, 2 }, + [CameraPositionOpcode.UnknownF9] = new[] { 2, 2, 2 }, + //ConditionalRestart is special + }; + _conditionalRestartOp = CameraPositionOpcode.ConditionalRestart; + } + + public CameraPositionScript(byte[] bytecode) : base(bytecode) { + } + + protected override CameraPositionOpcode FromByte(byte b) { + return (CameraPositionOpcode)b; + } + } + + public class CameraFocusScript : CameraScript { + + static CameraFocusScript() { + _opParams = new() { + [CameraFocusOpcode.UnknownD8] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.SetActiveIdleCamera] = new[] { 1 }, + [CameraFocusOpcode.UnknownDE] = new[] { 1 }, + [CameraFocusOpcode.UnknownE0] = new[] { 1, 1 }, + [CameraFocusOpcode.TransitionToIdle] = new[] { 1 }, + [CameraFocusOpcode.UnknownE3] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.TransitionToAttackerJoint] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.TransitionToTargetJoint] = new[] { 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.UnknownE6] = new[] { 2, 2, 2, 1 }, + [CameraFocusOpcode.TransitionToAttackerView] = new[] { 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.UnknownEA] = new[] { 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.UnknownEC] = new[] { 1, 1, 2, 2, 2, 1 }, + [CameraFocusOpcode.UnknownF0] = new[] { 1, 1, 2, 2, 2 }, + [CameraFocusOpcode.SetWait] = new[] { 1 }, + [CameraFocusOpcode.UnknownF8] = new[] { 1, 2, 2, 2 }, + [CameraFocusOpcode.UnknownF9] = new[] { 1, 2, 2, 2 }, + [CameraFocusOpcode.LoadPoint] = new[] { 2, 2, 2 }, + //ConditionalRestart is special + }; + _conditionalRestartOp = CameraFocusOpcode.ConditionalRestart; + } + + public CameraFocusScript(byte[] bytecode) : base(bytecode) { + } + + protected override CameraFocusOpcode FromByte(byte b) { + return (CameraFocusOpcode)b; + } + } + + public struct DecodedCameraOp where T : struct { + public T Opcode { get; set; } + public int[] Operands { get; set; } + } + + public enum CameraPositionOpcode { + UnknownD5 = 0xD5, + UnknownD6 = 0xD6, + UnknownD7 = 0xD7, + UnknownD8 = 0xD8, + UnknownD9 = 0xD9, + UnknownDA = 0xDA, + UnknownDB = 0xDB, + UnknownDC = 0xDC, + SetActiveIdleCamera = 0xDD, + UnknownDE = 0xDE, + UnknownDF = 0xDF, + UnknownE0 = 0xE0, + LoadIdleCameraPos = 0xE1, + UnknownE2 = 0xE2, + UnknownE3 = 0xE3, + TransitionToAttackerJoint = 0xE4, + TransitionToTargetJoint = 0xE5, + UnknownE6 = 0xE6, + UnknownE7 = 0xE7, + UnknownE9 = 0xE9, + UnknownEB = 0xEB, + UnknownEF = 0xEF, + UnknownF0 = 0xF0, + UnknownF1 = 0xF1, + UnknownF2 = 0xF2, + UnknownF3 = 0xF3, + Wait = 0xF4, + SetWait = 0xF5, + UnknownF7 = 0xF7, + UnknownF8 = 0xF8, + UnknownF9 = 0xF9, + ConditionalRestart = 0xFE, + ScriptEnd = 0xFF, + } + + public enum CameraFocusOpcode { + UnknownD8 = 0xD8, + UnknownD9 = 0xD9, + UnknownDB = 0xDB, + UnknownDC = 0xDC, + SetActiveIdleCamera = 0xDD, + UnknownDE = 0xDE, + UnknownDF = 0xDF, + UnknownE0 = 0xE0, + LoadIdleCameraPos = 0xE1, + TransitionToIdle = 0xE2, + UnknownE3 = 0xE3, + TransitionToAttackerJoint = 0xE4, + TransitionToTargetJoint = 0xE5, + UnknownE6 = 0xE6, + TransitionToAttackerView = 0xE8, + UnknownEA = 0xEA, + UnknownEC = 0xEC, + UnknownF0 = 0xF0, + Wait = 0xF4, + SetWait = 0xF5, + UnknownF8 = 0xF8, + UnknownF9 = 0xF9, + LoadPoint = 0xFA, + ConditionalRestart = 0xFE, + ScriptEnd = 0xFF, + } +}