Skip to content

Commit

Permalink
Add ForcesArchive direct loading/extraction
Browse files Browse the repository at this point in the history
No non-direct loading/extraction, generation, or HedgeArcPack support yet.
Those will all come soon.

This commit also slightly optimizes LWArchive stuff
  • Loading branch information
Radfordhound committed Nov 13, 2019
1 parent 818b0ca commit 16a24c0
Show file tree
Hide file tree
Showing 7 changed files with 562 additions and 67 deletions.
50 changes: 50 additions & 0 deletions HedgeLib/include/HedgeLib/Archives/ForcesArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once
#include "PACx.h"
#include "HedgeLib/Blob.h"

namespace hl
{
class Archive;
struct ForcesArchive
{
PACxV3Header Header;

// Each node in here contains another array of nodes, this time containing PACxV3DataEntries.
PACxV3NodeTree TypeTree;
};

// TODO

//HL_API void DAddForcesArchive(const Blob& blob, Archive& arc);
inline Blob DLoadForcesArchive(const char* filePath)
{
// Included for completeness
return DPACxLoadV3(filePath);
}

/*HL_API std::size_t DForcesArchiveGetFileCount(const Blob& blob,
bool includeProxies = true);*/

HL_API std::unique_ptr<const char*[]> DForcesArchiveGetSplitPtrs(
const Blob& blob, std::size_t& splitCount);

HL_API void DExtractForcesArchive(const Blob& blob, const char* dir);

//HL_API void SaveForcesArchive(const Archive& arc,
// const char* filePath, bool bigEndian,
// std::uint32_t splitLimit = HL_PACX_DEFAULT_SPLIT_LIMIT);

#ifdef _WIN32
inline Blob DLoadForcesArchive(const nchar* filePath)
{
// Included for completeness
return DPACxLoadV3(filePath);
}

HL_API void DExtractForcesArchive(const Blob& blob, const nchar* dir);

//HL_API void SaveForcesArchive(const Archive& arc,
// const nchar* filePath, bool bigEndian,
// std::uint32_t splitLimit = HL_PACX_DEFAULT_SPLIT_LIMIT);
#endif
}
6 changes: 3 additions & 3 deletions HedgeLib/include/HedgeLib/Archives/LWArchive.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ namespace hl
{
PACxV2DataNode Header;

// Each node in here contains another array of nodes, this time containing hl_PACxV2DataEntries.
ArrayOffset32<PACxV2Node> TypeTree;
// Each node in here contains another array of nodes, this time containing PACxV2DataEntries.
PACxV2NodeTree TypeTree;

inline void EndianSwap()
{
Header.EndianSwap();
// Header is already swapped in INDPACxFixDataNodeV2
Swap(TypeTree);
}

Expand Down
104 changes: 103 additions & 1 deletion HedgeLib/include/HedgeLib/Archives/PACx.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ namespace hl
enum PACXV2_DATA_FLAGS
{
PACXV2_DATA_FLAGS_NONE = 0,
PACXV2_DATA_FLAGS_NO_DATA = 0x80 // Indicates that this entry contains no data
PACXV2_DATA_FLAGS_NOT_HERE = 0x80 // Indicates that this entry contains no data
};

struct PACxV2DataEntry
Expand Down Expand Up @@ -91,6 +91,8 @@ namespace hl

HL_STATIC_ASSERT_SIZE(PACxV2Node, 8);

using PACxV2NodeTree = ArrayOffset32<PACxV2Node>;

struct PACxV2DataNode
{
BINAV2NodeHeader Header; // Contains general information on this node.
Expand All @@ -116,6 +118,88 @@ namespace hl

HL_STATIC_ASSERT_SIZE(PACxV2DataNode, 0x20);

using PACxV3SplitTable = ArrayOffset64<StringOffset64>;

enum PACxV3DataType : std::uint64_t
{
PACXV3_DATA_TYPE_REGULAR_FILE = 0,
PACXV3_DATA_TYPE_NOT_HERE = 1,
PACXV3_DATA_TYPE_BINA_FILE = 2
};

struct PACxV3DataEntry
{
std::uint32_t Unknown1; // Date Modified or Hash??
std::uint32_t DataSize;
std::uint64_t Unknown2; // Always 0? Unknown1 from PACxV2DataEntry??
DataOffset64<std::uint8_t> Data;
std::uint64_t Unknown3; // Always 0? Unknown2 from PACxV2DataEntry??
StringOffset64 Extension;
std::uint64_t DataType; // Probably actually just a single byte with 7 bytes of padding.
};

HL_STATIC_ASSERT_SIZE(PACxV3DataEntry, 0x30);

struct PACxV3Node
{
StringOffset64 Name;
DataOffset64<std::uint8_t> Data;
DataOffset64<std::int32_t> ChildIndices;
std::int32_t ParentIndex;
std::int32_t GlobalIndex;
std::int32_t DataIndex;
std::uint16_t ChildCount;
std::uint8_t HasData;
std::uint8_t FullPathSize; // Not counting this node's name.
};

HL_STATIC_ASSERT_SIZE(PACxV3Node, 0x28);

struct PACxV3NodeTree
{
std::uint32_t NodeCount;
std::uint32_t DataNodeCount;
DataOffset64<PACxV3Node> Nodes;
DataOffset64<std::int32_t> DataNodeIndices;
};

HL_STATIC_ASSERT_SIZE(PACxV3NodeTree, 0x18);

struct PACxV3Header
{
std::uint32_t Signature; // "PACx"
std::uint8_t Version[3]; // Version Number.
std::uint8_t EndianFlag; // 'B' for Big Endian, 'L' for Little Endian.
std::uint32_t Unknown1; // Date Modified or Hash??
std::uint32_t FileSize;
std::uint32_t NodesSize;
std::uint32_t SplitsInfoSize;
std::uint32_t DataEntriesSize;
std::uint32_t StringTableSize; // The size of the string table in bytes, including padding.
std::uint32_t DataSize;
std::uint32_t OffsetTableSize; // The size of the offset table in bytes, including padding.
std::uint16_t Type;
std::uint16_t Unknown2; // Always 0x108?
std::uint32_t SplitCount;

inline void EndianSwap()
{
Swap(Unknown1);
Swap(FileSize);
Swap(NodesSize);
Swap(SplitsInfoSize);
Swap(DataEntriesSize);
Swap(StringTableSize);
Swap(DataSize);
Swap(OffsetTableSize);
Swap(Type);
Swap(Unknown2);
Swap(SplitCount);
}
};

HL_STATIC_ASSERT_SIZE(PACxV3Header, 0x30);

// TODO: PACx V4 Support
//struct PACxV4Header
//{
Expand All @@ -140,8 +224,10 @@ namespace hl
bool includeProxies = true);

HL_API Blob DPACxReadV2(File& file);
HL_API Blob DPACxReadV3(File& file);
HL_API Blob DPACxRead(File& file);
HL_API Blob DPACxLoadV2(const char* filePath);
HL_API Blob DPACxLoadV3(const char* filePath);
HL_API Blob DPACxLoad(const char* filePath);

HL_API void PACxStartWriteV2(File& file, bool bigEndian);
Expand Down Expand Up @@ -171,6 +257,21 @@ namespace hl
DPACxGetDataV2(blob)));
}

HL_API const void* DPACxGetDataV3(const Blob& blob);

template<typename T>
inline const T* DPACxGetDataV3(const Blob& blob)
{
return static_cast<const T*>(DPACxGetDataV3(blob));
}

template<typename T>
inline T* DPACxGetDataV3(Blob& blob)
{
return static_cast<T*>(const_cast<void*>(
DPACxGetDataV3(blob)));
}

HL_API const void* DPACxGetData(const Blob& blob);

template<typename T>
Expand Down Expand Up @@ -199,6 +300,7 @@ namespace hl

#ifdef _WIN32
HL_API Blob DPACxLoadV2(const nchar* filePath);
HL_API Blob DPACxLoadV3(const nchar* filePath);
HL_API Blob DPACxLoad(const nchar* filePath);
HL_API void DExtractPACxArchive(const Blob& blob, const nchar* dir);
#endif
Expand Down
51 changes: 34 additions & 17 deletions HedgeLib/src/Archives/Archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "HedgeLib/Archives/PACx.h"
#include "HedgeLib/Archives/GensArchive.h"
#include "HedgeLib/Archives/LWArchive.h"
#include "HedgeLib/Archives/ForcesArchive.h"
#include "HedgeLib/IO/Path.h"
#include "HedgeLib/IO/File.h"
#include "HedgeLib/IO/BINA.h"
Expand Down Expand Up @@ -212,7 +213,7 @@ namespace hl
Blob blob;
switch (type)
{
// Unknown PACx
// Unknown PACx
case ArchiveType::PACx:
{
// Load the PAC
Expand All @@ -229,7 +230,9 @@ namespace hl
}
return blob;

// TODO: Add Forces archive support
case ArchiveType::PACxV3:
return blob;

// TODO: Add Tokyo 2020 archive support

default:
Expand All @@ -242,10 +245,14 @@ namespace hl
case ArchiveType::Gens:
return DLoadGensArchive(filePath);

// Lost World
// Lost World
case ArchiveType::PACxV2:
return DLoadLWArchive(filePath);

// Forces
case ArchiveType::PACxV3:
return DLoadForcesArchive(filePath);

// TODO: Add other Archive Types

default:
Expand Down Expand Up @@ -427,6 +434,12 @@ namespace hl
DAddLWArchive(blob, *this);
break;

// Forces
// TODO: Un-comment this
//case ArchiveType::PACxV3:
//DAddForcesArchive(blob, *this);
//break;

// TODO: Add other Archive Types

default:
Expand Down Expand Up @@ -668,18 +681,23 @@ namespace hl
{
switch (static_cast<ArchiveType>(blob.Type()))
{
// Unknown PACx
// Unknown PACx
case ArchiveType::PACx:
return DPACxGetFileCount(blob, includeProxies);

// Unleashed/Generations
// Unleashed/Generations
case ArchiveType::Gens:
return DGensArchiveGetFileCount(blob);

// Lost World
// Lost World
case ArchiveType::PACxV2:
return DLWArchiveGetFileCount(blob, includeProxies);

// Forces
// TODO: Un-comment this
//case ArchiveType::PACxV3:
//return DForcesArchiveGetFileCount(blob, includeProxies);

// TODO: Add other Archive Types

default:
Expand All @@ -699,27 +717,26 @@ namespace hl
DExtractPACxArchive(blob, dir);
return;

// .ar/.pfd (Unleashed/Generations)
// .ar/.pfd (Unleashed/Generations)
case ArchiveType::Gens:
DExtractGensArchive(blob, dir);
return;

// .pac V2 (LW)
// .pac V2 (LW)
case ArchiveType::PACxV2:
DExtractLWArchive(blob, dir);
return;

// .pac V3 (Forces)
// .pac V3 (Forces)
case ArchiveType::PACxV3:
// TODO: Forces Archives
//DExtractForcesArchive(blob, dir);
break;
DExtractForcesArchive(blob, dir);
return;

// .pac V4 (Tokyo 2020)
case ArchiveType::PACxV4:
// .pac V4 (Tokyo 2020)
//case ArchiveType::PACxV4:
// TODO: Tokyo 2020 Archives
//DExtractTokyoArchive(blob, dir);
break;
//return;
}

// Attempt to auto-detect archive type
Expand Down Expand Up @@ -761,11 +778,11 @@ namespace hl

// Extract splits
std::size_t splitCount;
if (rootExists && (static_cast<std::uint16_t>(type)&
if (rootExists && (static_cast<std::uint16_t>(type) &
static_cast<std::uint16_t>(ArchiveType::PACx)))
{
// Get splits list from data in root PAC
std::unique_ptr<const char* []> splits = DPACxArchiveGetSplitPtrs(
std::unique_ptr<const char*[]> splits = DPACxArchiveGetSplitPtrs(
arc, splitCount);

// There are no splits to extract
Expand Down
Loading

0 comments on commit 16a24c0

Please sign in to comment.