initial commit
BiRabittoh andronacomarco@gmail.com
Thu, 28 Mar 2024 15:10:34 +0100
8 files changed,
177 insertions(+),
0 deletions(-)
A
.vscode/launch.json
@@ -0,0 +1,15 @@
+{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + ] +}
A
LICENSE
@@ -0,0 +1,21 @@
+MIT License + +Copyright (c) 2024 Marco Andronaco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
A
README.md
@@ -0,0 +1,17 @@
+# nds-converter + +## What is it? +A lightweight API-first tool to convert NDS save files (`.sav`) to and from the DeSmuME format (`.dsv`). + +## How do I use it? +First, you need a save file, either of the two formats will work. +Then, you can just use `curl`: + +``` +curl localhost:1111 -F file=@savefile.sav > savefile.dsv +``` + +The conversion works both ways. +``` +curl localhost:1111 -F file=@savefile.dsv > savefile.sav +```
A
go.mod
@@ -0,0 +1,5 @@
+module github.com/BiRabittoh/nds-converter + +go 1.22.1 + +require github.com/joho/godotenv v1.5.1
A
go.sum
@@ -0,0 +1,2 @@
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
A
nds-converter.go
@@ -0,0 +1,112 @@
+package main + +import ( + "encoding/hex" + "io" + "net/http" + "os" + "path/filepath" + "strconv" + + "github.com/joho/godotenv" +) + +type ConverterFunction func([]byte) []byte + +const ( + MB = 1 << 20 + DefaultMaxFileSize = 2 + DefaultPort = "1111" + TrimBytes = 122 + AdditionalBytesHex = "7C3C2D2D536E69702061626F7665206865726520746F2063726561746520612072617720736176206279206578636C7564696E672074686973204465536D754D4520736176656461746120666F6F7465723A0000010000000100030000000200000000000100000000007C2D4445534D554D4520534156452D7C" +) + +var ( + MaxFileSize int64 + AdditionalBytes []byte + ConverterFunctions = map[string]ConverterFunction{ + ".dsv": DsvToSav, + ".sav": SavToDsv, + } +) + +func main() { + // Load the .env file + err := godotenv.Load() + if err != nil { + println("Could not load .env file") + } + + port := os.Getenv("PORT") + if port == "" { + port = DefaultPort + } + + fileSizeString := os.Getenv("MAX_SIZE_MB") + MaxFileSize, err = strconv.ParseInt(fileSizeString, 10, 64) + if err != nil { + MaxFileSize = DefaultMaxFileSize + } + MaxFileSize *= MB + + AdditionalBytes, _ = hex.DecodeString(AdditionalBytesHex) + + http.HandleFunc("/", uploadHandler) + println("Listening on port " + port) + http.ListenAndServe(":"+port, nil) +} + +func DsvToSav(input []byte) []byte { + return input[:len(input)-TrimBytes] +} + +func SavToDsv(input []byte) []byte { + return append(input, AdditionalBytes...) +} + +func uploadHandler(w http.ResponseWriter, r *http.Request) { + // Parse the multipart form data + err := r.ParseMultipartForm(MaxFileSize) + if err != nil { + http.Error(w, "Error parsing form", http.StatusBadRequest) + return + } + + r.Body = http.MaxBytesReader(w, r.Body, MaxFileSize) + + // Get the file from the request + file, h, err := r.FormFile("file") + if err != nil { + http.Error(w, "Error retrieving file from form data", http.StatusBadRequest) + return + } + defer file.Close() + + // Check file size + if h.Size > MaxFileSize { + http.Error(w, "File size exceeds 1MB", http.StatusBadRequest) + return + } + + var outputContent []byte + + // Check file extension + fileExt := filepath.Ext(h.Filename) + converterFunction, exists := ConverterFunctions[fileExt] + if !exists { + http.Error(w, "Invalid file format, only .dsv and .sav files are allowed", http.StatusBadRequest) + return + } + + content, err := io.ReadAll(file) + if err != nil { + http.Error(w, "Error reading file content", http.StatusInternalServerError) + return + } + outputContent = converterFunction(content) + + w.Header().Set("Content-Length", strconv.Itoa(len(outputContent))) + w.Header().Set("Content-Type", "application/octet-stream") + w.WriteHeader(http.StatusOK) + w.Write(outputContent) +}