101
Releases / Re: [FF8PC-Steam] Batch extraction of backgrounds
« on: 2018-12-12 15:48:52 »
If you're still interested in batch background extraction, then I'm installing Qt now and will fire unofficial build of Deling with mass export
This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
Hi Colly. Sorry for replying after such a looong time. Do you know if Hammerhead 1.4 works with Ep. Duscae and Platinum Demo? I am currently modding those two demo. Are you in the FFXV hacking discord ?You may want to take a look here:
private struct SoundEntry
{
public int Size;
public int Offset;
public byte[] UNK; //12
public byte[] WAVFORMATEX; //18
public ushort SamplesPerBlock;
public ushort ADPCM;
public byte[] ADPCMCoefSets; //28
}
private struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
private static SoundEntry[] soundEntries;
public static int soundEntriesCount;
internal static void DEBUG_SoundAudio()
{
string path = Path.Combine(Memory.FF8DIR, "..\\Sound\\audio.fmt");
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
soundEntries = new SoundEntry[br.ReadUInt32()];
fs.Seek(36, SeekOrigin.Current);
for (int i = 0; i < soundEntries.Length-1; i++)
{
int sz = br.ReadInt32();
if(sz == 0) {
fs.Seek(34, SeekOrigin.Current); continue; }
soundEntries[i] = new SoundEntry()
{
Size = sz,
Offset = br.ReadInt32(),
UNK = br.ReadBytes(12),
WAVFORMATEX = br.ReadBytes(18),
SamplesPerBlock = br.ReadUInt16(),
ADPCM = br.ReadUInt16(),
ADPCMCoefSets = br.ReadBytes(28)
};
}
}
soundEntriesCount = soundEntries.Length;
}
fs.Seek(soundEntries[soundID].Offset, SeekOrigin.Begin); //seek to raw buffer location in audio.dat thanks to audio.fmt pointer
List<byte[]> sfxBufferList = new List<byte[]>(); //this will be used as an dynamic array, I'm just too lazy
sfxBufferList.Add(Encoding.ASCII.GetBytes("RIFF")); //let's start with magic RIFF
sfxBufferList.Add(BitConverter.GetBytes
(soundEntries[soundID].Size + 36)); //now the size read from FMT + 36
sfxBufferList.Add(Encoding.ASCII.GetBytes("WAVEfmt ")); //now the WAVEfmt (there's a space, so it takes eight bytes)
sfxBufferList.Add(BitConverter.GetBytes
(18 + 0)); //eighteen
sfxBufferList.Add(soundEntries[soundID].WAVFORMATEX); //now encode full WAVEFORMATEX struct packed
sfxBufferList.Add(Encoding.ASCII.GetBytes("data")); //now add 'data' in ascii
sfxBufferList.Add(BitConverter.GetBytes(soundEntries[soundID].Size)); //now put the size again from FMT
byte[] rawBuffer = br.ReadBytes(soundEntries[soundID].Size); //obviously read audio.dat
sfxBufferList.Add(rawBuffer); //and add it on the bottom
byte[] sfxBuffer = sfxBufferList.SelectMany(x => x).ToArray(); //now cast every byte in byte list to array
using NAudio;
using NAudio.Wave;
internal static void PlaySound(int soundID)
{
if (soundEntries == null)
return;
if (soundEntries[soundID].Size == 0) return;
using (FileStream fs = new FileStream(Path.Combine(Memory.FF8DIR, "..\\Sound\\audio.dat"), FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
fs.Seek(soundEntries[soundID].Offset, SeekOrigin.Begin);
GCHandle gc = GCHandle.Alloc(soundEntries[soundID].WAVFORMATEX, GCHandleType.Pinned); //it's not recommended way in C#, but I'm again- lazy
WAVEFORMATEX format = (WAVEFORMATEX)Marshal.PtrToStructure(gc.AddrOfPinnedObject(), typeof(WAVEFORMATEX));
gc.Free();
byte[] rawBuffer = br.ReadBytes(soundEntries[soundID].Size);
//passing WAVEFORMATEX struct params makes playing all sounds possible
RawSourceWaveStream raw = new RawSourceWaveStream(new MemoryStream(rawBuffer), new AdpcmWaveFormat((int)format.nSamplesPerSec, format.nChannels ));
var a = WaveFormatConversionStream.CreatePcmStream(raw);
WaveOut waveout = new WaveOut(); //you have to use new instance for EVERY sound played
waveout.Init(a); //as said in documentation- init is supposed to be called once.
waveout.Play();
}
}
SoundEffect se = new SoundEffect(sfxBuffer, 22050, AudioChannels.Mono);
se.Play(1.0f, 0.0f, 0.0f);
22050Hz + mono are the only parameters that makes playing ADPCM forced sound as natural as possible
I'm being able to see the codes inside the places, how can I see the encounters in worldmap?
00400298
00403A30
00469548
004823FB
00484C23
00487384
0048A25C
0048A2FE
0048E4A9
00491267
004913CF
004B3AD7
004B5761
004CAB0A
00505E51
00506255
0050A713
0056AAF3
00667B10
00670E38
006757F8
006E5F39
008454C1
00B68783
As for resetting the counter, I'm a little less sure of where to put it but was thinking the battle results screen as even when escaping that screen tends to come up. Does anyone know where that section starts from?
FFBattleInitSystem+1E 66 83 3D C6 8F CD 01 08 cmp _StateGlobal, 8
where _stateGlobal (global variable) is at 01CD8FC6 in .data in-memory00B6D0A8 <- I recommend this one, it's section between imports for PE and strings that are allocated on 256 bytes
00B6D1B2
00B6D2B7
00B6D3C1
00B6D4BE
00B6D5C4
00B6D6C7
00B76918
00B7CDA8
00B7CF13
00B7D10B
00B7D18B
00B7D20B
00B7D2A7
00B7D507
00B7D6A7
00B7D713
00B7D90B
00B7D98B
00B7DA0B
00B7DC23
00B7DE1B
00B7DE9B
00B7DF1B
00B8A53B
00B8B3A7
00B8C1F3
00B8C2F7
00B8C3FB
00B8C514
00B8C618
00B8C71C
00B8C832
00B8C936
00B8CA3A
00B8CB53
00B8CC57
00B8CD5B
00B8CE73
00B8CF77
00B8D07B
00B8D192
00B8D296
00B8D39A
00B8DCCA
00B96EE4
00B976AF
00B97885
00B97A05
00B97B85
00B97DA4
00B97F25
00B980A5
00B98225
00B98563
00B986E3
00B9888E
00B98A0F
00B98B8F
00B98D0F
00B98E8F
00B9A50D
char[8] FLBD0101;
uint IMCHpointer;
uint unk;
padding[8]; //?
uint UnknownSHCHpointer; //0x28 + this
uint UnknownSPCHpointer; //0x28 + this
uint UnknownBTCHpointer; //0x28 + this
uint UnknownANCH0pointer; //0x28 + this
IMCH imch;
char[4] IMCH
uint IMCHsize; //including this + IMCH header
uint CountOfFrames; //This is global count of pointers to graphics;sprites;frames in file. It means one FLB can contain many graphics
uint SpritePointer[CountOfFrames]; //first one usually points to 0x00, so just: IMCHpointer+IMCHsize+pointer;
byte[20] unknown_maybeChecksum?;
uint Unknown;
ushort width;
ushort height;
TEXTUREdata;
Size: 1
Size: 2
Size: 3
Size: 4 <--
Size: 6
Size: 8
Size: 12
Size: 16
Size: 24
Size: 32
Size: 48
Size: 64
Size: 96
Size: 128
Size: 192
This looks awesome Maki, do you have a tutorial on how to set it up, do you need to download unity etcOh right, sorry. I'll make sure to write the tutorial to set-up everything ASAP
porting FF8 to PS4 aswell, via Custom UNITY engine?So... I'm working in gamedev industry and have access to official PS4+XOne SDK*, but even with that it's not so easy. For Unity you need to have full commercial license. It's better for XNA however, as you need only the target file. Anyway, everything's possible.
import sys
import struct
#CONFIG
filePath = sys.argv[1]
#END OF CONFIG
print(filePath)
#XmbDocument
f = open(filePath, 'rb')
xmb2 = f.read(4)
eof = struct.unpack('L', f.read(4))[0]
flags = struct.unpack('H', f.read(2))[0]
version = struct.unpack('H', f.read(2))[0]
rootElement = struct.unpack('L', f.read(4))[0]
f.seek(rootElement-4,1) #xmbDocument is casted now to XmbElement
#XmbElement
xmb2Element_elementStartOffset = struct.unpack('L', f.read(4))[0] #v6
xmb2Element_attributeStartOffset = struct.unpack('L', f.read(4))[0]
xmb2Element_elementCount = struct.unpack('H', f.read(2))[0]
xmb2Element_attributeCount = struct.unpack('H', f.read(2))[0] >> 8
f.seek(-12,1) #going back to XMB2Element root
AbsoluteXmb2ElementRoot = f.tell() # &v3->elementStartOffset_
IndexPointer = 0 #v4
SignedPointer = AbsoluteXmb2ElementRoot + 4 * IndexPointer + xmb2Element_elementStartOffset
f.seek(SignedPointer, 0)
SignedPointerValue = struct.unpack('i', f.read(4))[0]
v7 = AbsoluteXmb2ElementRoot + (4*IndexPointer) + xmb2Element_elementStartOffset + SignedPointerValue
#(char *)(v7 + *(_DWORD *)(v7 + 4) + 4i64);
v8Pointer = v7 + 4
f.seek(v8Pointer, 0)
v8PointerValue = struct.unpack('I', f.read(4))[0]
_ = v7 + v8PointerValue + 4
f.seek(_,0)
#v9 = &v8[*(_DWORD *)v8];
v8 = struct.unpack('i', f.read(4))[0]
f.seek(-4,1)
v8Pointer = f.tell()
v9 = v8Pointer + v8
f.seek(v9, 0)
v9Pt = struct.unpack('B', f.read(1))[0]
if v9Pt == 1:
f.seek(v9+5, 0)
v10Pt = struct.unpack('I', f.read(4))[0]
v10 = v9+v10Pt+5
f.seek(v10,0)
objectName = f.read().split('\0')[0]
print('Index: ' + str(IndexPointer) + ': ' + objectName)
ffxv_s.exe:0x1EE = 0x20
(replacing 0x60, 2nd bit from 1 to 0)00000000 identifier dd ?
00000004 fileSize dd ?
00000008 flags dw ?
0000000A version dw ?
0000000C rootElementOffset dd ? <-- relative to root
SQEX::Luminous::Xml::Xmb2Element::GetElementByName(Xmb2Element el, char* name);
example:uint64 pointer = XMBdocument + elementStartOffset + i*4;
uint64 v7 = pointer + *(pointer);
0: reference
1: entities_.common_textures_list
2: entities_.initialize1_list
3: entities_.system_no_autoload
4: entities_
5: hasTransform_
6: bool
7: position_
8: float4
9: rotation_
10: scaling_
11: float
12: canManipulate_
13: sourcePath_
14: common/initialize1.ebex
15: string
16: name_
17: dff7c814-9313-45a9-b8fd-5fc7ca670db7
18: isTemplateTraySourceReference_
19: isShared_
20: startupLoad_
21: object
22: initialize1
23: SQEX.Ebony.Framework.Entity.EntityPackage
24:
25: filePath_
26: textures/ebony_required_textures.txt
27: isAutoLoad_
28: common_textures_list
29: Black.Entity.Data.EarcResourceList
30: common/initialize1_list.txt
31: initialize1_list
32: common/system_no_autoload.txt
33: system_no_autoload
34: objects
35: package
void ** SQEX::Luminous::AssetManager::LmArcFileSystem::OpenFile (LmArcFileSystem *this, LmAssetID *i_id, LmEAssetLoadPriority priority)
File read:
SQEX::Luminous::AssetManager::LmArcFileSystem::ReadFile -> determines if compressed or not by LmArcInterface::FindAssetInfo ->
int64 SQEX::Luminous::AssetManager::LmArcFileSystem::ReadCompressed(LmArcFileSystem *this, FileSystemEntry *i_pFileEntry, ArcAssociation *i_pArchive /* this one is archive name in 80%*/, void *o_pAddress, unsigned int i_length, void **io_asyncHandle, LmEAssetLoadPriority priority, SQEX::Luminous::Core::IO::IFileDevice::FileAttribute attribute)
QWORD entry ; offset // It's in fact in code as LmArcCatalogShortEntry
DWORD arcMountIndex
BYTE UNK ; undefined
BYTE UNK ; undefined
BYTE UNK ; undefined
BYTE UNK ; undefined
QWORD nameTypeHash dq ?
QWORD dataStart
std::string name
std::string pAbsolutePath
DWORD originalSize
DWORD compressedSize
USHORT flags
BYTE localizeType
BYTE localizeLocale
SHORT encryptKey
BYTE UNK
BYTE UNK