Polyphase Game Engine
Loading...
Searching...
No Matches
ContentPak.h
Go to the documentation of this file.
1#pragma once
2
3#include <stdint.h>
4#include <string>
5#include <vector>
6
7#include "PolyphaseAPI.h"
8
9// Content.pak -- a single-archive shipping format for Static builds.
10//
11// Phase 1 obfuscated loose files: contents were protected but the filenames and
12// directory tree stayed readable, and every asset still cost a directory entry
13// on the target's filesystem. A pak folds all of it into one file:
14//
15// [ header 32 bytes, plain -- just enough to find the index ]
16// [ data per-entry ContentObfuscation containers, concatenated ]
17// [ index entry table + path blob, itself obfuscated ]
18//
19// Only the index is resident at runtime; entry bytes are read and decoded on
20// demand, so console memory behaviour matches the loose-file path it replaces.
21// Because the index is obfuscated, the paths are hidden too -- a shipped pak
22// leaks neither content nor names.
23//
24// Entries are stored as the same containers Phase 1 writes, so Stream::ReadFile's
25// existing decode handles them unchanged; the pak layer only has to hand back the
26// right span of bytes. That also keeps truncated reads working: the keystream is
27// offset-addressable, so a capped read decodes a correct prefix.
28//
29// Lookups are path-keyed, which Phase 1 deliberately avoided. It is safe here
30// because the pak index is the single authority on naming -- keys are written by
31// the cook and read back verbatim, with no per-platform path canonicalisation to
32// disagree about. Hash hits are confirmed against the stored path, so a 64-bit
33// collision degrades to a miss rather than to silently serving the wrong asset.
34//
35// Multiple mounts stack rather than replace: Mount()/MountMemory() push a new
36// layer and Find() searches newest-first, so a downloaded content bundle can
37// shadow (or add to) the base pak without evicting it. MountMemory() exists
38// because some platforms have no writable storage at all for a downloaded
39// bundle (e.g. GameCube memory cards) -- the pak is read straight out of the
40// buffer it arrived in.
41
42namespace ContentPak
43{
44 static const uint32_t kHeaderSize = 32;
45
46 // Identifies one mounted pak. 0 is never a valid handle.
47 typedef uint32_t MountHandle;
48
49 // ---- Runtime -----------------------------------------------------------
50
51 // Reads the header and index of a pak on disk and pushes it as a new,
52 // top-priority mount. Safe to call when the file is absent -- returns 0 and
53 // leaves prior mounts (if any) untouched, so callers fall back to loose
54 // files or to whatever was already mounted.
55 POLYPHASE_API MountHandle Mount(const char* pakPath);
56
57 // Same, but for a pak that already lives in memory -- the only option on a
58 // platform with no writable storage for a downloaded bundle. If
59 // takeOwnership is true, ContentPak frees `data` with free() on Unmount, so
60 // the buffer must come from malloc (Stream and Http response bodies do); if
61 // false, the caller owns `data` and must keep it alive until Unmount.
62 // debugName is used only for logging.
63 POLYPHASE_API MountHandle MountMemory(const void* data, uint32_t size,
64 const char* debugName, bool takeOwnership);
65
66 // Removes one mount. Entries from other mounts are unaffected.
68
69 // Removes every mount.
71
72 // True if at least one pak is mounted.
74
75 POLYPHASE_API bool Exists(const char* path);
76
77 // Reads an entry's stored (still-obfuscated) bytes from whichever mount has
78 // it, newest first. `outData` is malloc'd so Stream can take ownership and
79 // free() it, matching SYS_AcquireFileData. `maxSize > 0` reads a prefix; the
80 // container header is accounted for.
81 POLYPHASE_API bool Read(const char* path, int32_t maxSize, char*& outData, uint32_t& outSize);
82
83 // Locates an entry's raw span for the seekable streaming reader, which needs
84 // its own handle rather than a whole-file read. outMount identifies which
85 // mount owns the entry, for GetPakPath / GetMountMemory.
86 POLYPHASE_API bool FindEntry(const char* path, MountHandle& outMount,
87 uint32_t& outDataOffset, uint32_t& outDataSize);
88
89 // Path a *file-backed* mount was opened from, for opening an independent
90 // streaming handle on the same archive. Returns "" for a memory mount or an
91 // unrecognised handle -- try GetMountMemory() in that case.
92 POLYPHASE_API const char* GetPakPath(MountHandle handle);
93
94 // Raw buffer backing a *memory* mount, so the seekable reader can read
95 // straight out of it instead of opening a file. Returns false for a
96 // file-backed mount or an unrecognised handle.
97 POLYPHASE_API bool GetMountMemory(MountHandle handle, const uint8_t*& outData, uint32_t& outSize);
98
99 // Every key under `prefix`, merged across all mounts (each key reported
100 // once, from whichever mount would win a Find()). Needed because some
101 // content is discovered by walking a directory rather than by name -- the
102 // Vulkan global shaders are enumerated from Engine/Shaders/GLSL/bin/ -- and
103 // a packed build has no directory to walk.
104 POLYPHASE_API void List(const char* prefix, std::vector<std::string>& outKeys);
105
106 // ---- Cook --------------------------------------------------------------
107
109 {
110 std::string mKey; // canonical, package-relative (e.g. "Game/Assets/T.oct")
111 std::string mSourcePath; // absolute path to read from
112 };
113
114 // Writes `pakPath` from `files`. Each file is wrapped in a ContentObfuscation
115 // container (or passed through if already wrapped). Returns false on any I/O
116 // failure -- callers must not prune loose content unless this succeeded.
117 POLYPHASE_API bool Build(const char* pakPath, const std::vector<SourceFile>& files, uint32_t& outEntryCount);
118}
Export macros for Polyphase Engine symbols.
#define POLYPHASE_API
Definition PolyphaseAPI.h:31
Definition ContentPak.h:43
POLYPHASE_API MountHandle MountMemory(const void *data, uint32_t size, const char *debugName, bool takeOwnership)
Definition ContentPak.cpp:475
POLYPHASE_API bool FindEntry(const char *path, MountHandle &outMount, uint32_t &outDataOffset, uint32_t &outDataSize)
Definition ContentPak.cpp:615
POLYPHASE_API bool Build(const char *pakPath, const std::vector< SourceFile > &files, uint32_t &outEntryCount)
Definition ContentPak.cpp:689
POLYPHASE_API bool IsMounted()
Definition ContentPak.cpp:550
uint32_t MountHandle
Definition ContentPak.h:47
POLYPHASE_API bool Exists(const char *path)
Definition ContentPak.cpp:555
POLYPHASE_API void Unmount(MountHandle handle)
Definition ContentPak.cpp:533
POLYPHASE_API bool Read(const char *path, int32_t maxSize, char *&outData, uint32_t &outSize)
Definition ContentPak.cpp:630
POLYPHASE_API bool GetMountMemory(MountHandle handle, const uint8_t *&outData, uint32_t &outSize)
Definition ContentPak.cpp:567
POLYPHASE_API void UnmountAll()
Definition ContentPak.cpp:545
POLYPHASE_API const char * GetPakPath(MountHandle handle)
Definition ContentPak.cpp:560
POLYPHASE_API void List(const char *prefix, std::vector< std::string > &outKeys)
Definition ContentPak.cpp:577
POLYPHASE_API MountHandle Mount(const char *pakPath)
Definition ContentPak.cpp:390
Definition ContentPak.h:109
std::string mKey
Definition ContentPak.h:110
std::string mSourcePath
Definition ContentPak.h:111