diff --git a/.gitignore b/.gitignore index 8ce97a8..fb64f37 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ go.work #Others /bin +/src/bin diff --git a/src/build_script.ps1 b/src/build_script.ps1 index 69ffd1f..48af0c8 100644 --- a/src/build_script.ps1 +++ b/src/build_script.ps1 @@ -4,17 +4,17 @@ $env:GOARCH = "amd64" echo "Setting the target operating system to windows" $env:GOOS = "windows" echo "Building for windows x86-64" -go build -o ../bin/recoverdiscordcache_windows.exe +go build -o ./bin/recoverdiscordcache_windows.exe echo "Setting the target operating system to linux" $env:GOOS = "linux" echo "Building for linux x86-64" -go build -o ../bin/recoverdiscordcache_linux +go build -o ./bin/recoverdiscordcache_linux echo "Setting the target operating system to darwin (macOS)" $env:GOOS = "darwin" echo "Building for mac x86-64" -go build -o ../bin/recoverdiscordcache_mac +go build -o ./bin/recoverdiscordcache_mac echo "Resetting the environment variables" Remove-Item Env:\GOARCH diff --git a/src/go.mod b/src/go.mod index 9ed192b..383ef13 100644 --- a/src/go.mod +++ b/src/go.mod @@ -3,7 +3,5 @@ module azmekk/recoverdiscordcache go 1.20 require ( - github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/h2non/filetype v1.1.3 // indirect - golang.org/x/net v0.8.0 // indirect + github.com/h2non/filetype v1.1.3 ) diff --git a/src/go.sum b/src/go.sum index b581f1c..6d7d999 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,6 +1,2 @@ -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= diff --git a/src/main.go b/src/main.go index 45f215c..7ba6227 100644 --- a/src/main.go +++ b/src/main.go @@ -2,26 +2,28 @@ package main import ( "fmt" - "io/ioutil" "os" ) -var knownUnreadableFiles = []string{"index", "data_0", "data_1", "data_2", "data_3"} - func main() { fmt.Println("Locating discord cache folder") discordCacheFolder := getDiscordCacheFolderBasedOnOS() fmt.Println("Reading cache folder for all saved files.") - cachedFiles, err := ioutil.ReadDir(discordCacheFolder) + dirEntries, err := os.ReadDir(discordCacheFolder) if err != nil { fmt.Println("Error reading folder:", err) os.Exit(1) } fmt.Println("Recovering found files.") - for _, file := range cachedFiles { - readAndSeparateFile(file, discordCacheFolder) + for _, dirEntry := range dirEntries { + fileInfo, err := dirEntry.Info() + if err != nil { + fmt.Println("Error reading file ", dirEntry.Name()) + continue + } + readAndSeparateFile(fileInfo, discordCacheFolder) } fmt.Println("Done!") diff --git a/src/utils.go b/src/utils.go index 5fc6261..eff1385 100644 --- a/src/utils.go +++ b/src/utils.go @@ -2,18 +2,24 @@ package main import ( "bytes" + "errors" "fmt" "io/fs" - "io/ioutil" - "mime" "os" "path/filepath" "runtime" - "github.com/gabriel-vasile/mimetype" "github.com/h2non/filetype" ) +type cachedFileData struct { + fileExtension string + startingByte int +} + +var knownUnreadableFiles = [...]string{"index", "data_0", "data_1", "data_2", "data_3"} +var exeDir string = getExeDir() + func readAndSeparateFile(fileInfo fs.FileInfo, discordCacheFolder string) { if fileInfo.IsDir() { return @@ -26,49 +32,51 @@ func readAndSeparateFile(fileInfo fs.FileInfo, discordCacheFolder string) { } filePath := filepath.Join(discordCacheFolder, fileInfo.Name()) - exeDir := getExeDir() - buffer := getFileBuffer(filePath, fileInfo) - fileType, unknownFileTypeErr, firstFileByte := getFileMimeType(buffer) - buffer = buffer[firstFileByte:] - if unknownFileTypeErr != nil { + + buffer, err := getFileBuffer(filePath, fileInfo) + if err != nil { + fmt.Println("Could not read buffer for file: ", fileInfo.Name()) return - } else if fileType == "application/octet-stream" { + } + + fileData, err := getNewFileData(buffer) + + if err != nil && err.Error() == "unknown filetype" { return } - fileExtensions := getFileExtensions(fileType, filePath) - if len(fileExtensions) == 0 { + if err != nil { + fmt.Println("Encountered error while trying to determine file type for file: ", fileInfo.Name()) return } - fileExtension := fileExtensions[0] - saveDir := getSaveDirAndCreateIfNotExists(exeDir, fileExtension) - sameFileAlreadyExists, newFilePath := getNewFilePath(buffer, saveDir, fileInfo.Name(), fileExtension, 0) + saveDir := getOrCreateSaveDir(exeDir, fileData.fileExtension) - if sameFileAlreadyExists { + buffer = buffer[fileData.startingByte:] + fileExists, newFilePath := getNewFilePath(buffer, saveDir, fileInfo.Name(), fileData.fileExtension) + + if fileExists { return } - err := ioutil.WriteFile(newFilePath, buffer, 0644) + err = os.WriteFile(newFilePath, buffer, 0644) if err != nil { - fmt.Println(fmt.Sprintf("Error writing file %s:%s", filePath, err)) + + fmt.Println("Error writing file: ", filePath, " due to err: ", err) return } } -func doesFileNameExist(filePath string) bool { +func fileNameExists(filePath string) bool { _, err := os.Stat(filePath) - if os.IsNotExist(err) { - return false - } - return true + return !os.IsNotExist(err) } func isSameFile(buffer []byte, existingFilePath string) bool { - fileData, err := ioutil.ReadFile(existingFilePath) + fileData, err := os.ReadFile(existingFilePath) if err != nil { - fmt.Println(fmt.Sprintf("Error reading file to compare with buffer %s: %s", existingFilePath, err)) - os.Exit(1) + fmt.Println("Error reading file: ", existingFilePath, "to compare with buffer due to err: ", err) + return true } if bytes.Equal(fileData, buffer) { @@ -78,67 +86,53 @@ func isSameFile(buffer []byte, existingFilePath string) bool { } } -func getNewFilePath(buffer []byte, saveDir string, fileName string, fileExtension string, depth int) (bool, string) { - depth++ - filePath := filepath.Join(saveDir, fileName+fileExtension) +func getNewFilePath(buffer []byte, saveDir string, fileName string, fileExtension string) (bool, string) { + filePath := filepath.Join(saveDir, fmt.Sprintf("%s.%s", fileName, fileExtension)) - fileNameAlreadyExists := doesFileNameExist(filePath) - - if fileNameAlreadyExists && isSameFile(buffer, filePath) { + if fileNameExists(filePath) && isSameFile(buffer, filePath) { return true, "" - } else if fileNameAlreadyExists { - fileName = fmt.Sprintf("%s_%d", fileName, depth) - return getNewFilePath(buffer, saveDir, fileName, fileExtension, depth) } - return false, filePath -} + i := 0 + for fileNameExists(filePath) { + i++ + filePath = fmt.Sprintf("%d_%s", i, filePath) + } -func detectOS() string { - os := runtime.GOOS - return os + return false, filePath } -func getFileBuffer(filePath string, fileInfo fs.FileInfo) []byte { +func getFileBuffer(filePath string, fileInfo fs.FileInfo) ([]byte, error) { file, err := os.Open(filePath) + if err != nil { - fmt.Println(fmt.Sprintf("Error opening file %s: %s", filePath, err)) - os.Exit(1) + fmt.Println("Error opening file: ", filePath, " due to err: ", err) + return nil, errors.New("") } defer file.Close() buffer := make([]byte, fileInfo.Size()) _, err = file.Read(buffer) if err != nil { - fmt.Println(fmt.Sprintf("Error reading file %s: %s", filePath, err)) - os.Exit(1) + fmt.Println("Error reading file: ", filePath, " due to err: ", err) + return nil, errors.New("") } - return buffer + return buffer, nil } func getExeDir() string { exePath, err := os.Executable() if err != nil { - fmt.Println("Error retrieving executable path:", err) + fmt.Println("Error retrieving executable path: ", err) os.Exit(1) } return filepath.Dir(exePath) } -func getFileExtensions(fileType string, filePath string) []string { - fileExtensions, err := mime.ExtensionsByType(fileType) - if err != nil { - fmt.Println(fmt.Sprintf("Error getting extension for mime type %s for file %s: %s", fileType, filePath, err)) - os.Exit(1) - } - - return fileExtensions -} - -func getSaveDirAndCreateIfNotExists(exeDir string, fileExtension string) string { - saveDir := filepath.Join(exeDir, fileExtension[1:]) +func getOrCreateSaveDir(exeDir string, fileExtension string) string { + saveDir := filepath.Join(exeDir, fileExtension) _, err := os.Stat(saveDir) if os.IsNotExist(err) { os.Mkdir(saveDir, 0755) @@ -182,26 +176,37 @@ func getDiscordCacheFolderBasedOnOS() string { } } -func getFileMimeType(buffer []byte) (string, error, int) { +func getNewFileData(buffer []byte) (cachedFileData, error) { if runtime.GOOS == "windows" { - return mimetype.Detect(buffer).String(), nil, 0 + return getFileExtension(buffer) } else { - return detectUnixFileMIMEType(buffer) + return getFileExtensionLinux(buffer) + } +} + +func getFileExtension(buffer []byte) (cachedFileData, error) { + fileInfo, _ := filetype.Match(buffer) + if fileInfo == filetype.Unknown { + return cachedFileData{}, errors.New("unknown filetype") } + + return cachedFileData{fileInfo.Extension, 0}, nil } -func detectUnixFileMIMEType(buffer []byte) (string, error, int) { - for i := 0; i < 400; i++ { +func getFileExtensionLinux(buffer []byte) (cachedFileData, error) { + for i := 0; i < 70; i++ { if i >= len(buffer) { break } - kind, _ := filetype.Match(buffer[i:]) + fileInfo, _ := filetype.Match(buffer[i:]) - if kind != filetype.Unknown { - return kind.MIME.Value, nil, i + if fileInfo == filetype.Unknown { + continue } + + return cachedFileData{fileInfo.Extension, 0}, nil } - return "", fmt.Errorf("Unknown filetype"), 0 + return cachedFileData{}, errors.New("unknown filetype") }