Skip to content

Commit

Permalink
rpak v6, s2 audio support
Browse files Browse the repository at this point in the history
the only known use of rpak v6 is the titanfall 2 pre-alpha mp tech test
there aren't many changes from v6 to v7:
- no compression
- no patches
- some fields in the header went from uint32_t to uint16_t

also season 2 audio support using the same offsets/structs as s3
  • Loading branch information
r-ex committed Feb 14, 2022
1 parent 911ac6b commit 272c100
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 9 deletions.
5 changes: 3 additions & 2 deletions Legion/MilesLib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ void MilesLib::MountBank(const string& Path)
Assets.Add(Hashing::XXHash::HashString(Name), Asset);
}
}
else if (BankHeader.Version == 32) {
// S3
else if (BankHeader.Version >= 28 && BankHeader.Version <= 32) {
// S2 -> S3
ReaderStream->SetPosition(*(uint64_t*)(uintptr_t(&BankHeader) + 0x48));
const auto SourcesCount = *(uint32_t*)(uintptr_t(&BankHeader) + 0x98);
const auto NameTableOffset = *(uint64_t*)(uintptr_t(&BankHeader) + 0x70);
Expand All @@ -317,6 +317,7 @@ void MilesLib::MountBank(const string& Path)
}
}
else {
g_Logger.Warning("Unknown MBNK Version: %i\n", BankHeader.Version);
throw new std::exception("Unknown MBNK version!");
}
}
Expand Down
4 changes: 3 additions & 1 deletion Legion/RpakAssetExtract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1368,7 +1368,9 @@ List<List<DataTableColumnData>> RpakLib::ExtractDataTable(const RpakLoadAsset& A

// todo: season 3 and earlier will not have "Unk8", but all later builds do,
// in order to maintain compatibility, something must be used to determine which version of dtbl this is
col.Unk8 = Reader.Read<uint64_t>();
if(Asset.Version == RpakGameVersion::Apex)
col.Unk8 = Reader.Read<uint64_t>(); // old apex datatables do not have this

col.Type = Reader.Read<uint32_t>();
col.RowOffset = Reader.Read<uint32_t>();

Expand Down
115 changes: 115 additions & 0 deletions Legion/RpakLib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,8 @@ bool RpakLib::MountRpak(const string& Path, bool Dump)
return this->MountApexRpak(Path, Dump);
case (uint32_t)RpakGameVersion::Titanfall:
return this->MountTitanfallRpak(Path, Dump);
case (uint32_t)RpakGameVersion::R2TT:
return this->MountR2TTRpak(Path, Dump);
default:
return false;
}
Expand Down Expand Up @@ -900,6 +902,102 @@ bool RpakLib::ParseTitanfallRpak(const string& RpakPath, std::unique_ptr<IO::Mem
return true;
}

bool RpakLib::ParseR2TTRpak(const string& RpakPath, std::unique_ptr<IO::MemoryStream>& ParseStream)
{
auto Reader = IO::BinaryReader(ParseStream.get(), true);
auto RpakRoot = IO::Path::GetDirectoryName(RpakPath);
auto Header = Reader.Read<RpakHeaderV6>();
auto File = &this->LoadedFiles[this->LoadedFileIndex++];

// Default version is apex, 0x8, must make sure this is set.
File->Version = RpakGameVersion::R2TT;

uint32_t StarpakLen = Header.StarpakReferenceSize;
while (StarpakLen > 0)
{
auto Starpak = Reader.ReadCString();

if (Starpak.Length() > 0)
{
auto Path = IO::Path::Combine(RpakRoot, IO::Path::GetFileName(Starpak));
this->MountStarpak(Path, this->LoadedFileIndex - 1, File->StarpakReferences.Count(), false);
File->StarpakReferences.EmplaceBack(Path);
}

StarpakLen -= Starpak.Length() + sizeof(char);
}

List<RpakVirtualSegment> VirtualSegments;
List<RpakVirtualSegmentBlock> Pages;
List<RpakUnknownBlockThree> UnknownBlockThrees;
List<RpakTitanfallAssetEntry> AssetEntries;

for (uint32_t i = 0; i < Header.VirtualSegmentCount; i++)
{
VirtualSegments.EmplaceBack(Reader.Read<RpakVirtualSegment>());
}
for (uint32_t i = 0; i < Header.PageCount; i++)
{
Pages.EmplaceBack(Reader.Read<RpakVirtualSegmentBlock>());
}
for (uint32_t i = 0; i < Header.DescriptorCount; i++)
{
UnknownBlockThrees.EmplaceBack(Reader.Read<RpakUnknownBlockThree>());
}
for (uint32_t i = 0; i < Header.AssetEntryCount; i++)
{
AssetEntries.EmplaceBack(Reader.Read<RpakTitanfallAssetEntry>());
}

ParseStream->Seek(sizeof(RpakDescriptor) * Header.GuidDescriptorCount, IO::SeekOrigin::Current);
ParseStream->Seek(sizeof(RpakFileRelation) * Header.UnknownSixthBlockCount, IO::SeekOrigin::Current);

// 7th and 8th blocks are weird and useless
ParseStream->Seek(sizeof(uint32_t) * Header.UnknownSeventhBlockCount, IO::SeekOrigin::Current);
ParseStream->Seek(Header.UnknownEighthBlockCount, IO::SeekOrigin::Current);

auto BufferRemaining = ParseStream->GetLength() - ParseStream->GetPosition();

uint64_t Offset = 0;
for (uint32_t i = 0; i < Header.PageCount; i++)
{
File->SegmentBlocks.EmplaceBack(Offset, Pages[i].DataSize);
Offset += Pages[i].DataSize;
}

for (auto& Asset : AssetEntries)
{
RpakApexAssetEntry NewAsset{};
NewAsset.OptimalStarpakOffset = (uint64_t)(-1);

std::memcpy(&NewAsset, &Asset, 40);
std::memcpy(((uint8_t*)&NewAsset) + 48, ((uint8_t*)&Asset) + 40, 32);

File->AssetHashmap.Add(Asset.NameHash, NewAsset);
}
File->StartSegmentIndex = 0;
File->SegmentData = std::make_unique<uint8_t[]>(BufferRemaining);
File->SegmentDataSize = BufferRemaining;

ParseStream->Read(File->SegmentData.get(), 0, BufferRemaining);

if (this->LoadedFileIndex == 1)
{
auto BasePath = IO::Path::GetDirectoryName(RpakPath);
auto FileNameNoExt = IO::Path::GetFileNameWithoutExtension(RpakPath);

// Trim off the () if exists
if (FileNameNoExt.Contains("("))
FileNameNoExt = FileNameNoExt.Substring(0, FileNameNoExt.IndexOf("("));

auto FinalPath = IO::Path::Combine(BasePath, FileNameNoExt);

this->LoadFileQueue.EmplaceBack(string::Format("%s.rpak", FinalPath.ToCString()));
}

return true;
}

void RpakLib::MountStarpak(const string& Path, uint32_t FileIndex, uint32_t StarpakIndex, bool Optimal)
{
auto& File = this->LoadedFiles[FileIndex];
Expand Down Expand Up @@ -1033,6 +1131,23 @@ bool RpakLib::MountTitanfallRpak(const string& Path, bool Dump)
return ParseTitanfallRpak(Path, ResultStream);
}

bool RpakLib::MountR2TTRpak(const string& Path, bool Dump)
{
auto Reader = IO::BinaryReader(IO::File::OpenRead(Path));
auto Header = Reader.Read<RpakHeaderV6>();

// rpak v6 doesn't seem to support compression

auto Stream = std::make_unique<IO::MemoryStream>();

Reader.GetBaseStream()->SetPosition(0);
Reader.GetBaseStream()->CopyTo(Stream.get());

Stream.get()->SetPosition(0);

return ParseR2TTRpak(Path, Stream);
}

RpakLoadAsset::RpakLoadAsset(uint64_t NameHash, uint32_t FileIndex, uint32_t AssetType, uint32_t SubHeaderIndex, uint32_t SubHeaderOffset, uint32_t SubHeaderSize, uint32_t RawDataIndex, uint32_t RawDataOffset, uint64_t StarpakOffset, uint64_t OptimalStarpakOffset, RpakGameVersion Version)
: NameHash(NameHash), FileIndex(FileIndex), RpakFileIndex(FileIndex), AssetType(AssetType), SubHeaderIndex(SubHeaderIndex), SubHeaderOffset(SubHeaderOffset), SubHeaderSize(SubHeaderSize), RawDataIndex(RawDataIndex), RawDataOffset(RawDataOffset), StarpakOffset(StarpakOffset), OptimalStarpakOffset(OptimalStarpakOffset), Version(Version)
{
Expand Down
43 changes: 37 additions & 6 deletions Legion/RpakLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ struct RpakApexHeader
uint16_t Version;
uint8_t Flags;
bool IsCompressed;

uint8_t Hash[0x10];
uint64_t CreatedFileTime;
uint64_t Hash;

uint64_t CompressedSize;
uint64_t EmbeddedStarpakOffset;
Expand Down Expand Up @@ -61,8 +61,8 @@ struct RpakTitanfallHeader
uint32_t Magic;
uint16_t Version;
uint16_t Flags;

uint8_t Hash[0x10];
uint64_t CreatedFileTime;
uint64_t Hash;

uint64_t CompressedSize;
uint64_t Padding1;
Expand All @@ -84,6 +84,34 @@ struct RpakTitanfallHeader
uint32_t UnknownEighthBlockCount;
};

struct RpakHeaderV6
{
uint32_t Magic;
uint16_t Version;
uint16_t Flags;

uint64_t CreatedFileTime;
uint64_t Hash;

uint64_t FileSize;
uint64_t Padding;
uint64_t Padding2;

uint32_t StarpakReferenceSize;
uint32_t VirtualSegmentCount; // * 0x10
uint32_t PageCount; // * 0xC

uint32_t DescriptorCount;
uint32_t AssetEntryCount;
uint32_t GuidDescriptorCount;
uint32_t UnknownSixthBlockCount;

uint32_t UnknownSeventhBlockCount;
uint32_t UnknownEighthBlockCount;
uint32_t what;
};


struct RpakDescriptor
{
uint32_t PageIdx;
Expand Down Expand Up @@ -198,8 +226,9 @@ struct RpakSegmentBlock

enum class RpakGameVersion : uint32_t
{
Titanfall = 0x7,
Apex = 0x8,
R2TT = 0x6, // Titanfall 2 Tech Test
Titanfall = 0x7, // Titanfall 2
Apex = 0x8, // Apex Legends
};

class RpakFile
Expand Down Expand Up @@ -447,4 +476,6 @@ class RpakLib
bool ParseApexRpak(const string& RpakPath, std::unique_ptr<IO::MemoryStream>& ParseStream);
bool MountTitanfallRpak(const string& Path, bool Dump);
bool ParseTitanfallRpak(const string& RpakPath, std::unique_ptr<IO::MemoryStream>& ParseStream);
bool MountR2TTRpak(const string& Path, bool Dump);
bool ParseR2TTRpak(const string& RpakPath, std::unique_ptr<IO::MemoryStream>& ParseStream);
};

0 comments on commit 272c100

Please sign in to comment.