all repos — nds-converter @ 65fd5c5d446b6f9b9140ae33580f7311bec3167e

A lightweight save converter for NDS and DeSmuME.

minor refactor
Marco Andronaco andronacomarco@gmail.com
Sat, 30 Mar 2024 01:01:40 +0100
commit

65fd5c5d446b6f9b9140ae33580f7311bec3167e

parent

5b5911dcfac31e65d6c727444b88c5f700ca4ee3

6 files changed, 116 insertions(+), 120 deletions(-)

jump to
M DockerfileDockerfile

@@ -9,7 +9,7 @@ COPY go.mod go.sum ./

RUN go mod download # Transfer source code -COPY templates ./templates +COPY index.html ./index.html COPY *.go ./ # Build

@@ -21,9 +21,9 @@ RUN go test -v ./...

FROM scratch AS build-release-stage -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +#COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /dist /app WORKDIR /app -ENTRYPOINT ["/app/app"]+ENTRYPOINT ["/app/app"]
D backend.go

@@ -1,81 +0,0 @@

-package main - -import ( - "io" - "net/http" - "path/filepath" - "strconv" -) - -type ConverterFunction func([]byte) []byte - -const ( - MB = 1 << 20 - DefaultMaxFileSize = 2 - TrimBytes = 122 - AdditionalBytesHex = "7C3C2D2D536E69702061626F7665206865726520746F2063726561746520612072617720736176206279206578636C7564696E672074686973204465536D754D4520736176656461746120666F6F7465723A0000010000000100030000000200000000000100000000007C2D4445534D554D4520534156452D7C" -) - -var ( - MaxFileSize int64 - AdditionalBytes []byte - ConverterFunctions = map[string]ConverterFunction{ - ".dsv": DsvToSav, - ".sav": SavToDsv, - } -) - -func DsvToSav(input []byte) []byte { - return input[:len(input)-TrimBytes] -} - -func SavToDsv(input []byte) []byte { - return append(input, AdditionalBytes...) -} - -func postHandler(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) -}
A convert.go

@@ -0,0 +1,57 @@

+package main + +import ( + "encoding/hex" + "fmt" + "io" + "mime/multipart" + "path/filepath" +) + +type ConverterFunction func([]byte) []byte + +const ( + trimBytes = 122 + additionalBytesHex = "7C3C2D2D536E69702061626F7665206865726520746F2063726561746520612072617720736176206279206578636C7564696E672074686973204465536D754D4520736176656461746120666F6F7465723A0000010000000100030000000200000000000100000000007C2D4445534D554D4520534156452D7C" +) + +var ( + additionalBytes = MustDecode(hex.DecodeString(additionalBytesHex)) + converterFunctions = map[string]ConverterFunction{ + ".dsv": DsvToSav, + ".sav": SavToDsv, + } +) + +func MustDecode(content []byte, err error) []byte { + if err != nil { + panic(42) + } + return content +} + +func DsvToSav(input []byte) []byte { + return input[:len(input)-trimBytes] +} + +func SavToDsv(input []byte) []byte { + return append(input, additionalBytes...) +} + +func Convert(file multipart.File, h *multipart.FileHeader) ([]byte, error) { + // Check file extension + fileExt := filepath.Ext(h.Filename) + converterFunction, exists := converterFunctions[fileExt] + if !exists { + return nil, fmt.Errorf("invalid file format: only .dsv and .sav files are allowed") + } + + // Read file contents + content, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + // Return converted file + return converterFunction(content), nil +}
D frontend.go

@@ -1,27 +0,0 @@

-package main - -import ( - "bytes" - "embed" - "net/http" - "text/template" -) - -const templatesDirectory = "templates/" - -var ( - //go:embed templates/index.html - templates embed.FS - indexTemplate = template.Must(template.ParseFS(templates, templatesDirectory+"index.html")) -) - -func getHandler(w http.ResponseWriter) { - buf := &bytes.Buffer{} - err := indexTemplate.Execute(buf, nil) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - buf.WriteTo(w) -}
M nds-converter.gonds-converter.go

@@ -1,7 +1,7 @@

package main import ( - "encoding/hex" + _ "embed" "net/http" "os" "strconv"

@@ -10,7 +10,16 @@ "github.com/joho/godotenv"

) const ( - DefaultPort = "1111" + MB = 1 << 20 + DefaultmaxFileSize = 2 + DefaultPort = "1111" +) + +var ( + //go:embed index.html + indexPage []byte + maxFileSize int64 + maxFileSizeMB int64 ) func main() {

@@ -26,14 +35,12 @@ port = DefaultPort

} fileSizeString := os.Getenv("MAX_SIZE_MB") - MaxFileSize, err = strconv.ParseInt(fileSizeString, 10, 64) + maxFileSize, err = strconv.ParseInt(fileSizeString, 10, 64) if err != nil { - MaxFileSize = DefaultMaxFileSize + maxFileSize = DefaultmaxFileSize } - println("Max file size:", MaxFileSize, "MB.") - MaxFileSize *= MB - - AdditionalBytes, _ = hex.DecodeString(AdditionalBytesHex) + println("Max file size:", maxFileSize, "MB.") + maxFileSizeMB = maxFileSize * MB http.HandleFunc("/", mainHandler) println("Listening on port " + port)

@@ -48,6 +55,46 @@ return

case http.MethodPost: postHandler(w, r) default: - http.Error(w, "Method not supported", http.StatusBadRequest) + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func getHandler(w http.ResponseWriter) { + w.Write(indexPage) +} + +func postHandler(w http.ResponseWriter, r *http.Request) { + // Parse the multipart form data + err := r.ParseMultipartForm(maxFileSizeMB) + if err != nil { + http.Error(w, "Error parsing form", http.StatusBadRequest) + return } + + // Limit upload file size + r.Body = http.MaxBytesReader(w, r.Body, maxFileSizeMB) + + // 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 > maxFileSizeMB { + http.Error(w, "File size exceeds "+strconv.FormatInt(maxFileSize, 10)+"MB", http.StatusBadRequest) + return + } + + // Convert file + output, err := Convert(file, h) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Send response + w.Write(output) }