From c3b557419d044ebe5f2333414499f547f59dfe23 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Tue, 24 Feb 2026 20:52:16 +0900 Subject: [PATCH] chore: first commit --- .gitignore | 11 ++++ go.mod | 5 ++ go.sum | 2 + main.go | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bbdd58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Build output +launcher.exe + +# IDE +.idea/ +.vscode/ + +# OS +Thumbs.db +Desktop.ini +.DS_Store diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e03fd43 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module a301_launcher + +go 1.24.0 + +require golang.org/x/sys v0.41.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cdc9b1c --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= diff --git a/main.go b/main.go new file mode 100644 index 0000000..66e2cfc --- /dev/null +++ b/main.go @@ -0,0 +1,158 @@ +package main + +import ( + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "strings" + + "golang.org/x/sys/windows/registry" +) + +const ( + protocolName = "a301" + gameExeName = "A301.exe" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println("사용법:") + fmt.Println(" launcher.exe install 프로토콜 등록") + fmt.Println(" launcher.exe uninstall 프로토콜 제거") + fmt.Println(" launcher.exe a301://... 게임 실행") + os.Exit(1) + } + + arg := os.Args[1] + + switch { + case arg == "install": + if err := install(); err != nil { + fmt.Fprintf(os.Stderr, "설치 실패: %v\n", err) + os.Exit(1) + } + fmt.Println("a301:// 프로토콜이 등록되었습니다.") + + case arg == "uninstall": + if err := uninstall(); err != nil { + fmt.Fprintf(os.Stderr, "제거 실패: %v\n", err) + os.Exit(1) + } + fmt.Println("a301:// 프로토콜이 제거되었습니다.") + + case strings.HasPrefix(arg, protocolName+"://"): + if err := handleURI(arg); err != nil { + fmt.Fprintf(os.Stderr, "실행 실패: %v\n", err) + os.Exit(1) + } + + default: + fmt.Fprintf(os.Stderr, "알 수 없는 명령: %s\n", arg) + os.Exit(1) + } +} + +func launcherPath() (string, error) { + exe, err := os.Executable() + if err != nil { + return "", err + } + return filepath.Abs(exe) +} + +func install() error { + exePath, err := launcherPath() + if err != nil { + return fmt.Errorf("실행 파일 경로를 찾을 수 없습니다: %w", err) + } + + // HKCU\Software\Classes\a301 + key, _, err := registry.CreateKey( + registry.CURRENT_USER, + `Software\Classes\`+protocolName, + registry.SET_VALUE, + ) + if err != nil { + return fmt.Errorf("레지스트리 키 생성 실패: %w", err) + } + defer key.Close() + + if err := key.SetStringValue("", "URL:A301 Protocol"); err != nil { + return err + } + if err := key.SetStringValue("URL Protocol", ""); err != nil { + return err + } + + // shell\open\command + cmdKey, _, err := registry.CreateKey( + registry.CURRENT_USER, + `Software\Classes\`+protocolName+`\shell\open\command`, + registry.SET_VALUE, + ) + if err != nil { + return fmt.Errorf("command 키 생성 실패: %w", err) + } + defer cmdKey.Close() + + cmdValue := fmt.Sprintf(`"%s" "%%1"`, exePath) + if err := cmdKey.SetStringValue("", cmdValue); err != nil { + return err + } + + return nil +} + +func uninstall() error { + err := registry.DeleteKey(registry.CURRENT_USER, `Software\Classes\`+protocolName+`\shell\open\command`) + if err != nil && err != registry.ErrNotExist { + return err + } + err = registry.DeleteKey(registry.CURRENT_USER, `Software\Classes\`+protocolName+`\shell\open`) + if err != nil && err != registry.ErrNotExist { + return err + } + err = registry.DeleteKey(registry.CURRENT_USER, `Software\Classes\`+protocolName+`\shell`) + if err != nil && err != registry.ErrNotExist { + return err + } + err = registry.DeleteKey(registry.CURRENT_USER, `Software\Classes\`+protocolName) + if err != nil && err != registry.ErrNotExist { + return err + } + return nil +} + +func handleURI(rawURI string) error { + parsed, err := url.Parse(rawURI) + if err != nil { + return fmt.Errorf("URI 파싱 실패: %w", err) + } + + token := parsed.Query().Get("token") + if token == "" { + return fmt.Errorf("토큰이 없습니다") + } + + exePath, err := launcherPath() + if err != nil { + return err + } + gameDir := filepath.Dir(exePath) + gamePath := filepath.Join(gameDir, gameExeName) + + if _, err := os.Stat(gamePath); os.IsNotExist(err) { + return fmt.Errorf("게임 파일을 찾을 수 없습니다: %s", gamePath) + } + + cmd := exec.Command(gamePath, "-token", token) + cmd.Dir = gameDir + if err := cmd.Start(); err != nil { + return fmt.Errorf("게임 실행 실패: %w", err) + } + + fmt.Printf("게임이 실행되었습니다 (PID: %d)\n", cmd.Process.Pid) + return nil +}