-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main'
- Loading branch information
Showing
18 changed files
with
798 additions
and
244 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,5 +58,3 @@ string readFile(File &f) { | |
string name; | ||
return f.filename; | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
Dir_pack packs directories to .dir/.wad files | ||
*/ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/halamix2/stunt_gp_tools/pkg/dir" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
// flags | ||
var ( | ||
outputName string | ||
// verbose bool | ||
) | ||
|
||
func parseFlags() { | ||
pflag.StringVarP(&outputName, "output", "o", "", "name of the output file") | ||
// verbose | ||
pflag.Parse() | ||
} | ||
|
||
func usage() { | ||
fmt.Println("Packs dir archive used by Stunt GP and Worms Armageddon.") | ||
fmt.Println("Usage:\ndir_pack input_dir [input_dirs]") | ||
fmt.Println("Flags:") | ||
pflag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
parseFlags() | ||
args := pflag.Args() | ||
if len(args) < 1 { | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
if outputName != "" && len(args) > 1 { | ||
fmt.Println("Output name can only be used with one input directory") | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
for _, inputName := range args { | ||
if outputName == "" { | ||
// I know, Worms uses .dir extension, but this is primarily Stunt GP program after all | ||
outputName = strings.TrimSuffix(inputName, filepath.Ext(inputName)) + ".wad" | ||
} | ||
|
||
dir := dir.Dir{} | ||
|
||
walkFunc := getWalkFunc(&dir) | ||
err := filepath.Walk(inputName, walkFunc) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't get list of files: %s\n", outputName) | ||
os.Exit(1) | ||
} | ||
fmt.Printf("packing %d files\n", len(dir)) | ||
|
||
outputFile, err := os.Create(outputName) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't create output image file %s!\n", outputName) | ||
os.Exit(1) | ||
} | ||
|
||
err = dir.ToWriter(outputFile) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't save dir file %s: %s\n", outputName, err) | ||
os.Exit(1) | ||
} | ||
|
||
err = outputFile.Close() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't close output file %s: %s\n", outputName, err) | ||
os.Exit(1) | ||
} | ||
} | ||
} | ||
|
||
func getWalkFunc(dir *dir.Dir) filepath.WalkFunc { | ||
return func(path string, info fs.FileInfo, err error) error { | ||
if !info.IsDir() { | ||
// file | ||
data, err := os.ReadFile(path) | ||
if err != nil { | ||
return err | ||
} | ||
pathNormalized := strings.ReplaceAll(path, "/", "\\") | ||
split := strings.Split(pathNormalized, "\\") | ||
pathNormalized = strings.Join(split[1:], "\\") | ||
(*dir)[pathNormalized] = data | ||
} | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
Dir_unpack unpacks .dir/.wad files to directories | ||
*/ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/halamix2/stunt_gp_tools/pkg/dir" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
// flags | ||
var ( | ||
outputDirectory string | ||
) | ||
|
||
func parseFlags() { | ||
pflag.StringVarP(&outputDirectory, "output", "o", "", "name of the output directory") | ||
pflag.Parse() | ||
} | ||
|
||
func usage() { | ||
fmt.Println("Unpacks dir archive used by Stunt GP and Worms Armageddon.") | ||
fmt.Println("Usage:\ndir_unpack input_file [input_files]") | ||
fmt.Println("Flags:") | ||
pflag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
parseFlags() | ||
args := pflag.Args() | ||
if len(args) < 1 { | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
if outputDirectory != "" && len(args) > 1 { | ||
fmt.Println("Output name can only be used with one input directory") | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
for _, inputName := range args { | ||
if outputDirectory == "" { | ||
// I know, Worms uses .dir extension, but this is primarily Stunt GP program after all | ||
outputDirectory = strings.TrimSuffix(inputName, filepath.Ext(inputName)) | ||
} | ||
|
||
dirFile, err := dir.FromDir(inputName) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't read Dir file: %s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
err = os.MkdirAll(outputDirectory, 0750) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't create %s path: %s\n", outputDirectory, err) | ||
os.Exit(1) | ||
} | ||
|
||
err = saveFiles(dirFile) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Couldn't save files: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} | ||
} | ||
|
||
func saveFiles(dirFile dir.Dir) error { | ||
for archFilename, archData := range dirFile { | ||
archFilename = strings.ReplaceAll(archFilename, "\\", "/") | ||
outPath := filepath.Join(outputDirectory, archFilename) | ||
|
||
err := os.MkdirAll(path.Dir(outPath), 0750) | ||
if err != nil { | ||
return fmt.Errorf("couldn't create %s path: %s", outPath, err) | ||
} | ||
|
||
err = os.WriteFile(outPath, archData, 0666) | ||
if err != nil { | ||
return fmt.Errorf("couldn't save %s file: %s", outPath, err) | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Package dir implements functions used to manipulate Worms Armageddon / Stunt GP .dir / .wad files. | ||
package dir | ||
|
||
const testDir = "test_data/" | ||
|
||
// Dir is a base representation of a Dir archive | ||
type Dir map[string][]byte | ||
|
||
// HashTable stores whole hash table | ||
type HashTable struct { | ||
Header [4]byte | ||
Entries [1024]uint32 | ||
} | ||
|
||
// Header contains all data at the beginning of a file | ||
type Header struct { | ||
Magic [4]byte | ||
Size uint32 | ||
DirectoryAddress uint32 | ||
} | ||
|
||
// FileMetadata contains all metadata stored in the file list | ||
type FileMetadata struct { | ||
NextHash uint32 | ||
DataOffset uint32 | ||
DataSize uint32 | ||
Filename string | ||
} | ||
|
||
// FileMetadataSmall contains metadata stored in the file list without the filename | ||
type FileMetadataSmall struct { | ||
NextHash uint32 | ||
DataOffset uint32 | ||
DataSize uint32 | ||
} | ||
|
||
// GetHash generates hash used by the DIR archives. Filename should be provided in ASCII encoding and without trailing \0 | ||
func GetHash(filename string) uint32 { | ||
var hash uint32 | ||
|
||
const hashBits uint32 = 10 | ||
const hashSize uint32 = 1 << hashBits | ||
|
||
for _, char := range filename { | ||
hash = (uint32(hash<<1) % hashSize) | uint32(hash>>(hashBits-1)&1) | ||
hash = uint32(hash+uint32(char)) % hashSize | ||
} | ||
|
||
return hash | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package dir | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestHashingFunction(t *testing.T) { | ||
// names should be provided without trailing \0, but the tests also check for that | ||
testCases := []struct { | ||
path string | ||
expectedHash uint32 | ||
}{ | ||
{ | ||
path: "", | ||
expectedHash: 0, | ||
}, | ||
{ | ||
path: "a", | ||
expectedHash: 97, | ||
}, | ||
{ | ||
path: "test", | ||
expectedHash: 655, | ||
}, | ||
{ | ||
path: "test\x00", | ||
expectedHash: 287, | ||
}, | ||
{ | ||
path: "graphics24\\cars\\car15\\b15grid5.pc", | ||
expectedHash: 341, | ||
}, | ||
{ | ||
path: "replays\\track19.rpl", | ||
expectedHash: 738, | ||
}, | ||
} | ||
|
||
for _, testCase := range testCases { | ||
hash := GetHash(testCase.path) | ||
if hash != testCase.expectedHash { | ||
t.Fatalf("HashingFunction: path: %s, expected hash: %d, got %d", testCase.path, testCase.expectedHash, hash) | ||
} | ||
} | ||
} |
Oops, something went wrong.