Compare commits
11 commits
6a0e75d614
...
6f54963287
Author | SHA1 | Date | |
---|---|---|---|
6f54963287 | |||
467cbea8d4 | |||
a773f267f7 | |||
b060f1de5e | |||
18a0e73a91 | |||
f65698fcb6 | |||
7af7fc427b | |||
8d07e6d307 | |||
b23431cdb2 | |||
170a46b6bf | |||
ebfca0fbb9 |
10 changed files with 199 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -546,3 +546,4 @@ FodyWeavers.xsd
|
|||
.idea/**
|
||||
*.s2pk
|
||||
s2pk.toml
|
||||
dist/**
|
||||
|
|
24
Config.cs
Normal file
24
Config.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
namespace S2PK;
|
||||
|
||||
public class Config
|
||||
{
|
||||
public Destinations Paths { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the configuration from the specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the configuration file.</param>
|
||||
/// <returns>The loaded configuration.</returns>
|
||||
public static Config Load(string path)
|
||||
{
|
||||
var file = File.ReadAllText(path);
|
||||
var config = Toml.ToModel<Config>(file);
|
||||
return config ?? throw new FileNotFoundException("Configuration file not found.");
|
||||
}
|
||||
}
|
||||
|
||||
public class Destinations
|
||||
{
|
||||
public string Sims2 { get; set; } = string.Empty;
|
||||
public string Sims3 { get; set; } = string.Empty;
|
||||
}
|
2
GlobalUsing.cs
Normal file
2
GlobalUsing.cs
Normal file
|
@ -0,0 +1,2 @@
|
|||
global using S2PK;
|
||||
global using Tomlyn;
|
26
Makefile
Normal file
26
Makefile
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Makefile for building and packaging the Sims 2 .s2pk CLI tool
|
||||
|
||||
APP_NAME = s2pk
|
||||
CONFIGURATION = Release
|
||||
RUNTIME_LINUX = linux-x64
|
||||
RUNTIME_MAC = osx-x64
|
||||
OUTPUT_DIR = ./dist
|
||||
|
||||
.PHONY: all clean build-linux build-mac package-linux package-mac
|
||||
|
||||
all: build-linux build-mac package-linux package-mac
|
||||
|
||||
clean:
|
||||
rm -rf bin obj $(OUTPUT_DIR)
|
||||
|
||||
build-linux:
|
||||
dotnet publish -c $(CONFIGURATION) -r $(RUNTIME_LINUX) --self-contained true /p:PublishSingleFile=true -o $(OUTPUT_DIR)/$(APP_NAME)-linux
|
||||
|
||||
build-mac:
|
||||
dotnet publish -c $(CONFIGURATION) -r $(RUNTIME_MAC) --self-contained true /p:PublishSingleFile=true -o $(OUTPUT_DIR)/$(APP_NAME)-mac
|
||||
|
||||
package-linux:
|
||||
tar -czvf $(OUTPUT_DIR)/$(APP_NAME)-linux.tar.gz -C $(OUTPUT_DIR)/$(APP_NAME)-linux .
|
||||
|
||||
package-mac:
|
||||
tar -czvf $(OUTPUT_DIR)/$(APP_NAME)-mac.tar.gz -C $(OUTPUT_DIR)/$(APP_NAME)-mac .
|
|
@ -1,4 +1,4 @@
|
|||
namespace S2Pkg;
|
||||
namespace S2PK;
|
||||
|
||||
using System.IO.Compression;
|
||||
|
||||
|
@ -6,11 +6,25 @@ public static class PackageManager
|
|||
{
|
||||
const string PackageExtension = ".package";
|
||||
const string S2pkExtension = ".s2pk";
|
||||
const string S3pkExtension = ".s3pk";
|
||||
|
||||
public static void PackPackages(string source, string output)
|
||||
/// <summary>
|
||||
/// Returns the extension for the package file.
|
||||
/// </summary>
|
||||
/// <param name="ts3">Whether to use the Sims 3 extension.</param>
|
||||
/// <returns>The extension for the package file.</returns>
|
||||
static string ExtensionHandler(bool ts3 = false) => ts3 ? S3pkExtension : S2pkExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Packs a directory into a package file.
|
||||
/// </summary>
|
||||
/// <param name="source">The source directory.</param>
|
||||
/// <param name="package">The output package file.</param>
|
||||
public static void PackPackages(string source, string package, bool ts3 = false)
|
||||
{
|
||||
var dir = new DirectoryInfo(source);
|
||||
var file = new FileInfo(output);
|
||||
var file = new FileInfo(package);
|
||||
var extension = ExtensionHandler(ts3);
|
||||
|
||||
if (!dir.Exists)
|
||||
{
|
||||
|
@ -36,8 +50,36 @@ public static class PackageManager
|
|||
Console.WriteLine($"Created {file.FullName} with {packageFiles.Length} files.");
|
||||
}
|
||||
|
||||
public static void UnpackPackages(string package, string destination)
|
||||
/// <summary>
|
||||
/// Unpacks a package file into a destination directory.
|
||||
/// </summary>
|
||||
/// <param name="package">The path to the package file.</param>
|
||||
/// <param name="destination">The destination directory. If not provided, the destination is read from the configuration file.</param>
|
||||
/// <param name="ts3">Whether to unpack for The Sims 3.</param>
|
||||
public static void UnpackPackages(string package, string destination = "", bool ts3 = false)
|
||||
{
|
||||
var extension = ExtensionHandler(ts3);
|
||||
|
||||
// If destination is not provided, read from configuration file
|
||||
if (string.IsNullOrEmpty(destination))
|
||||
{
|
||||
var config = Config.Load("s2pk.toml"); // TODO: Config file should be in home directory
|
||||
|
||||
// If The Sims 3 is not specified, use the default destination
|
||||
if (!ts3)
|
||||
destination = config.Paths.Sims2;
|
||||
else
|
||||
destination = config.Paths.Sims3;
|
||||
}
|
||||
|
||||
// Check if destination directory exists
|
||||
if (!Directory.Exists(destination))
|
||||
{
|
||||
Console.Error.WriteLine("Destination directory does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var file = new FileInfo(package);
|
||||
var dir = new DirectoryInfo(destination);
|
||||
|
||||
|
@ -47,6 +89,20 @@ public static class PackageManager
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!file.FullName.Contains(extension))
|
||||
{
|
||||
Console.Error.WriteLine("Package file is not a valid.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for extension mismatch
|
||||
if (file.FullName.Contains(S3pkExtension) && !ts3)
|
||||
{
|
||||
Console.Error.WriteLine("Package is for The Sims 3 but unpacking for The Sims 2.");
|
||||
return;
|
||||
}
|
||||
|
||||
dir.Create(); // Ensures directory exists
|
||||
|
||||
using var archive = ZipFile.OpenRead(file.FullName);
|
||||
|
|
21
Program.cs
21
Program.cs
|
@ -1,8 +1,12 @@
|
|||
using S2Pkg;
|
||||
using System.CommandLine;
|
||||
|
||||
var rootCommand = new RootCommand("The Sims 2 .s2pk Package Manager");
|
||||
|
||||
var sims3option = new Option<bool>(
|
||||
aliases: ["--ts3"],
|
||||
description: "Switch to The Sims 3 mode.",
|
||||
getDefaultValue: () => false);
|
||||
|
||||
var sourceOption = new Option<string>(
|
||||
aliases: ["--source", "-s"],
|
||||
description: "Source directory containing .package files")
|
||||
|
@ -20,10 +24,10 @@ var packCommand = new Command("pack", "Packs .package files into a .s2pk archive
|
|||
outputOption
|
||||
};
|
||||
|
||||
packCommand.SetHandler((source, output) =>
|
||||
packCommand.SetHandler((source, output, sims3) =>
|
||||
{
|
||||
PackageManager.PackPackages(source, output);
|
||||
}, sourceOption, outputOption);
|
||||
PackageManager.PackPackages(source, output, sims3);
|
||||
}, sourceOption, outputOption, sims3option);
|
||||
|
||||
var packageOption = new Option<string>(
|
||||
aliases: ["--package", "-p"],
|
||||
|
@ -32,8 +36,7 @@ var packageOption = new Option<string>(
|
|||
|
||||
var destinationOption = new Option<string>(
|
||||
aliases: ["--destination", "-d"],
|
||||
description: "Destination directory (e.g., The Sims 2 Downloads folder)")
|
||||
{ IsRequired = true };
|
||||
description: "Destination directory (e.g., The Sims 2 Downloads folder)");
|
||||
|
||||
// Unpack command
|
||||
var unpackCommand = new Command("unpack", "Unpacks a .s2pk archive to the Downloads folder")
|
||||
|
@ -42,10 +45,10 @@ var unpackCommand = new Command("unpack", "Unpacks a .s2pk archive to the Downlo
|
|||
destinationOption
|
||||
};
|
||||
|
||||
unpackCommand.SetHandler((package, destination) =>
|
||||
unpackCommand.SetHandler((package, destination, sims3) =>
|
||||
{
|
||||
PackageManager.UnpackPackages(package, destination);
|
||||
}, packageOption, destinationOption);
|
||||
PackageManager.UnpackPackages(package, destination, sims3);
|
||||
}, packageOption, destinationOption, sims3option);
|
||||
|
||||
// Register commands
|
||||
rootCommand.AddCommand(packCommand);
|
||||
|
|
50
README.md
50
README.md
|
@ -2,18 +2,33 @@
|
|||
|
||||
S2PK, or Sims 2 Package Manager, aims to provide a simple cross-platform solution to package management.
|
||||
|
||||
## 💡 Why This Exists
|
||||
## ❓ Why This Exists
|
||||
|
||||
S2PK was created to address the need for a reliable and efficient cross-platform package management system for The Sims 2 on Linux and macOS. Players on these platforms often face challenges in managing mods due to the lack of a unified solution that their Windows counterparts solved a long time ago.
|
||||
While The Sims 2 runs well on Linux through Wine, especially with Lutris setups, and natively from macOS, only the Windows community had varies mod managers and installers compared to their cross-platform counterparts. Later installments addressed this dilemma, but TS2 never got that treatment.
|
||||
|
||||
`s2pk` aims to fill this gap: a no-nonsense CLI utility for packaging, sharing, and managing mods across all platforms. It’s small, clean, and plays nicely with scripts, backups, and version control. In theory, it would be possible to use it on legacy front ends.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- First-class CLI support for modders on non-Windows platforms
|
||||
- Custom default unpacking directory for The Sims 2 & 3
|
||||
- Pack `.packages` into portable `.s2pk` archives
|
||||
|
||||
## 🛣️ Project Roadmap
|
||||
|
||||
| Phase | Goal | Status |
|
||||
| ----- | ------------------------------------------- | ------ |
|
||||
| v0.1 | Core package manager | ✅ |
|
||||
| v0.2 | Config file with default destination | 🔜 |
|
||||
| v0.2 | Config file with default paths | ✅ |
|
||||
| v0.3 | Sims 3 support with `s3pk` extention | 🔜 |
|
||||
| v0.x | Target .NET 10 | 🔜 |
|
||||
| v1.0 | Stable "Release" version with documentation | 🔜 |
|
||||
|
||||
## 🎯 Stretch Goals
|
||||
|
||||
- [ ] Manifest validation
|
||||
- [ ] `s1pk` soft fork
|
||||
|
||||
## 🧩 Tech Stack
|
||||
|
||||
- .NET 8.0
|
||||
|
@ -21,7 +36,28 @@ S2PK was created to address the need for a reliable and efficient cross-platform
|
|||
- System.CommandLine for CLI parsing (no external libraries)
|
||||
- Pure backend logic (no UI planned)
|
||||
|
||||
## Example
|
||||
## 📐 Design Principles
|
||||
|
||||
- Stay true to UNIX design philosophy
|
||||
- Simple with no bloat or feature creep
|
||||
- Portable and clean
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
You can build yourself with `make`, or use the installer script:
|
||||
|
||||
```shell
|
||||
./install.sh ./dist/s2pk-linux/s2pk
|
||||
```
|
||||
|
||||
Make sure `~/.local/bin` is in your `PATH`:
|
||||
|
||||
```shell
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
## 🗜️ Usage
|
||||
|
||||
```shell
|
||||
s2pk pack -s ./mods -o output.s2pk
|
||||
|
@ -37,7 +73,7 @@ s2pk unpack -p ./output.s2pk -d "%USERPROFILE%\Documents\EA Games\The Sims 2\Dow
|
|||
| ------------ | ---------------- | ---------------------------------------- |
|
||||
| Minor Update | Every 3–6 months | Small enhancements, non-breaking changes |
|
||||
| Patch Update | As needed | Bug fixes, security updates |
|
||||
| Major Update[^1] | As needed | Framework upgrades, major refactors |
|
||||
| Major Update | As needed | Framework upgrades, major refactors |
|
||||
|
||||
- Reserve months: June (Mid-Year Chill) & December (End-Year Freeze)
|
||||
|
||||
|
@ -56,10 +92,6 @@ s2pk unpack -p ./output.s2pk -d "%USERPROFILE%\Documents\EA Games\The Sims 2\Dow
|
|||
- Critical vulnerabilities
|
||||
- Framework-breaking issues
|
||||
|
||||
## 🔍 Footnotes
|
||||
|
||||
- [^1]: At this early stage, major updates bump the minor version number. This usually involves re-targeting to the newest .NET LTS.
|
||||
|
||||
## 🗒️ License
|
||||
|
||||
I license this project under the GPL v3 license - see [LICENSE](LICENSE) for details.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>0.1.101</Version>
|
||||
<Version>0.3.101</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Authors>Tony Bark</Authors>
|
||||
|
@ -13,8 +13,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nett" Version="0.15.0" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="Tomlyn" Version="0.19.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
27
install.sh
Normal file
27
install.sh
Normal file
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Install s2pkg locally for current user (no sudo)
|
||||
# Usage: ./install.sh ./dist/s2pkg-linux/s2pkg
|
||||
|
||||
set -e
|
||||
|
||||
SOURCE_BIN="$1"
|
||||
INSTALL_DIR="$HOME/.local/bin"
|
||||
TARGET="$INSTALL_DIR/s2pkg"
|
||||
|
||||
if [[ ! -x "$SOURCE_BIN" ]]; then
|
||||
echo "❌ Error: Provide a valid built s2pkg binary as the first argument."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cp "$SOURCE_BIN" "$TARGET"
|
||||
chmod +x "$TARGET"
|
||||
|
||||
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||||
echo "⚠️ $INSTALL_DIR is not in your PATH. Consider adding this to your shell profile:"
|
||||
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
||||
else
|
||||
echo "✅ Installed s2pkg to $TARGET"
|
||||
echo "Run 's2pkg --help' to get started."
|
||||
fi
|
|
@ -1 +1,3 @@
|
|||
destination ≡ "path/to/destination"
|
||||
[paths]
|
||||
sims2 ≡ "path/to/sims2"
|
||||
sims3 ≡ "path/to/sims3"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue