Python library to read and write Silkroad Online PK2 files
Project description
PK2API
Based on: https://github.com/JellyBitz/SRO.PK2API
Python library to read and write data into the PK2 file format from Silkroad Online.
Features
- Fast search O(1) lookups
- Create new PK2 files
- Create new paths recursively if does not exist
- Full read/write support
- Bulk extract and import operations
- Archive comparison (diff) functionality
- Copy files/folders between archives
- Glob pattern matching
- Archive validation and integrity checks
- Command-line interface (CLI)
- Zero external dependencies (pure Python)
Installation
pip install pk2api
Or install from source:
pip install -e .
CLI Usage
After installation, the pk2 command is available:
# List all files in an archive
pk2 list Media.pk2
# List files matching a glob pattern
pk2 list Media.pk2 -p "**/*.txt"
# Extract entire archive
pk2 extract Media.pk2 -o ./output
# Extract specific folder
pk2 extract Media.pk2 -f data -o ./output
# Import directory into archive
pk2 add Media.pk2 ./files -t target
# Show archive statistics
pk2 info Media.pk2
# Validate archive integrity
pk2 validate Media.pk2
# Compare two archives
pk2 compare old.pk2 new.pk2 # Text diff output
pk2 compare old.pk2 new.pk2 -f json # JSON output for tools
pk2 cmp old.pk2 new.pk2 --quick # Size-only comparison (faster)
# Copy between archives
pk2 copy src.pk2 dst.pk2 path/file.txt # Copy single file
pk2 copy src.pk2 dst.pk2 "**/*.xml" # Copy files matching glob
pk2 copy src.pk2 dst.pk2 data/folder -r # Copy folder recursively
pk2 cp src.pk2 dst.pk2 folder -r -d backup # Copy to different destination
Python API Usage
from pk2api import Pk2Stream
key = "169841" # Default SRO key
# Read-only mode
with Pk2Stream("/path/to/Media.pk2", key, read_only=True) as pk2:
# Get file content
file = pk2.get_file("Type.txt")
if file:
content = file.get_content()
print(content.decode("utf-8"))
# List files from root folder
root = pk2.get_folder("")
print("Files:")
for path in root.files.keys():
print(f" - {path}")
# List folders from root folder
print("Folders:")
for path in root.folders.keys():
print(f" - {path}")
# Find files by glob pattern
for file in pk2.glob("**/*.txt"):
print(file.get_full_path())
# Iterate all files
for file in pk2.iter_files():
print(file.get_full_path())
# Get archive statistics
stats = pk2.get_stats()
print(f"Files: {stats['file_count']}, Folders: {stats['folder_count']}")
# Read-write mode
with Pk2Stream("/path/to/Media.pk2", key) as pk2:
# Add & remove folder
pk2.add_folder("test/new_folder")
pk2.remove_folder("test/new_folder")
# Add & remove file
pk2.add_file("test/new_file.txt", b"Hello World")
pk2.remove_file("test/new_file.txt")
# Bulk extract
pk2.extract_all("./output")
pk2.extract_folder("data", "./output")
# Bulk import from disk
pk2.import_from_disk("./files", "target/path")
# Validate archive integrity
result = pk2.validate()
print(f"Valid: {result['valid']}")
# Copy between archives
with Pk2Stream("source.pk2", key, read_only=True) as src:
with Pk2Stream("dest.pk2", key) as dst:
dst.copy_file_from(src, "path/to/file.txt")
dst.copy_folder_from(src, "data/folder")
dst.copy_files_from(src, ["file1.txt", "file2.txt"])
# Compare archives
from pk2api import compare_archives
with Pk2Stream("old.pk2", key, read_only=True) as old:
with Pk2Stream("new.pk2", key, read_only=True) as new:
result = compare_archives(old, new)
print(f"Added: {len(result.added_files)}")
print(f"Removed: {len(result.removed_files)}")
print(f"Modified: {len(result.modified_files)}")
API Reference
Pk2Stream
Main class for reading and writing PK2 archives.
Pk2Stream(path: str, key: str, read_only: bool = False)
Core Methods:
get_folder(path: str) -> Pk2Folder | None- Get folder by path (O(1))get_file(path: str) -> Pk2File | None- Get file by path (O(1))add_folder(path: str) -> bool- Create folder (recursive)add_file(path: str, data: bytes) -> bool- Add or update fileremove_folder(path: str) -> bool- Remove folder and contentsremove_file(path: str) -> bool- Remove fileclose()- Close the stream
Iteration Methods:
iter_files() -> Iterator[Pk2File]- Iterate all files in archiveiter_folders() -> Iterator[Pk2Folder]- Iterate all folders in archiveglob(pattern: str) -> Iterator[Pk2File]- Find files matching glob pattern
Utility Methods:
get_stats() -> dict- Return file/folder counts and sizesvalidate() -> dict- Check archive integrityextract_all(output_dir: str, progress=None)- Extract entire archiveextract_folder(folder_path: str, output_dir: str, progress=None)- Extract subfolderimport_from_disk(source_dir: str, target_path: str = "", progress=None)- Bulk import
Inter-Archive Copy Methods:
copy_file_from(source: Pk2Stream, source_path: str, target_path: str = None)- Copy single filecopy_folder_from(source: Pk2Stream, source_path: str, target_path: str = None, progress=None)- Copy folder recursivelycopy_files_from(source: Pk2Stream, paths: list, target_base: str = "", progress=None)- Copy multiple files
Pk2Folder
Represents a folder within a PK2 archive.
Properties:
name: str- Folder name (lowercase)original_name: str- Case-preserved name from archiveparent: Pk2Folder | None- Parent folderoffset: int- Byte offset in streamfiles: dict[str, Pk2File]- Files in this folderfolders: dict[str, Pk2Folder]- Subfolders
Methods:
get_full_path() -> str- Get full path from root (lowercase)get_original_path() -> str- Get full path with original case
Pk2File
Represents a file within a PK2 archive.
Properties:
name: str- File name (lowercase)original_name: str- Case-preserved name from archiveparent: Pk2Folder- Parent folderoffset: int- Byte offset in streamsize: int- File size in bytes
Methods:
get_full_path() -> str- Get full path from root (lowercase)get_original_path() -> str- Get full path with original caseget_content() -> bytes- Read file content
Comparison Functions
from pk2api import compare_archives, ComparisonResult, FileChange, FolderChange, ChangeType
compare_archives(source: Pk2Stream, target: Pk2Stream, compute_hashes: bool = True) -> ComparisonResult
ComparisonResult Properties:
added_files: list[FileChange]- Files added in targetremoved_files: list[FileChange]- Files removed from sourcemodified_files: list[FileChange]- Files modified between archives
ChangeType Enum:
ADDED,REMOVED,MODIFIED,UNCHANGED
Requirements
- Python 3.10+
Special Thanks!
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pk2api-1.5.0.tar.gz.
File metadata
- Download URL: pk2api-1.5.0.tar.gz
- Upload date:
- Size: 30.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af0496bfa5b7e0d0591e3272d214ddb41e13e58450f7131d24ce69b911ef8032
|
|
| MD5 |
3aca3d88bc9d3dafe0446f3daa0ed85a
|
|
| BLAKE2b-256 |
540387c3036c4645dbb4bffa84212a6272287317c3a243e2e49c25713a1b4461
|
Provenance
The following attestation bundles were made for pk2api-1.5.0.tar.gz:
Publisher:
publish.yml on dadav/pk2api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pk2api-1.5.0.tar.gz -
Subject digest:
af0496bfa5b7e0d0591e3272d214ddb41e13e58450f7131d24ce69b911ef8032 - Sigstore transparency entry: 832966082
- Sigstore integration time:
-
Permalink:
dadav/pk2api@1f05aa63861aae824c326b3731675c51b790228d -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/dadav
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1f05aa63861aae824c326b3731675c51b790228d -
Trigger Event:
push
-
Statement type:
File details
Details for the file pk2api-1.5.0-py3-none-any.whl.
File metadata
- Download URL: pk2api-1.5.0-py3-none-any.whl
- Upload date:
- Size: 30.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
635e12f0f62960ae7495b82b72a1555852dfe7185f84b98889db298848020bd3
|
|
| MD5 |
17d353aee9b43b9d976eb7ba6cb0c86e
|
|
| BLAKE2b-256 |
992b4b4cbed57e6249b9f286356b24b66fbcc6509a01eccd55bc63d8bdb0e64e
|
Provenance
The following attestation bundles were made for pk2api-1.5.0-py3-none-any.whl:
Publisher:
publish.yml on dadav/pk2api
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pk2api-1.5.0-py3-none-any.whl -
Subject digest:
635e12f0f62960ae7495b82b72a1555852dfe7185f84b98889db298848020bd3 - Sigstore transparency entry: 832966083
- Sigstore integration time:
-
Permalink:
dadav/pk2api@1f05aa63861aae824c326b3731675c51b790228d -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/dadav
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1f05aa63861aae824c326b3731675c51b790228d -
Trigger Event:
push
-
Statement type: