-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add zbm_packer * simplify
- Loading branch information
Showing
7 changed files
with
323 additions
and
23 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
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,156 @@ | ||
/* | ||
zbm_pack converts popular image formats to Gamewave .zbm images. | ||
*/ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
_ "image/jpeg" | ||
_ "image/png" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/iafan/cwalk" | ||
"github.com/namgo/GameWaveFans/pkg/zbm" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
// flags | ||
var ( | ||
outputName string | ||
) | ||
|
||
func parseFlags() { | ||
pflag.StringVarP(&outputName, "output", "o", "", "name of the output file") | ||
pflag.Parse() | ||
} | ||
|
||
func usage() { | ||
fmt.Println("Packs image to .zbm texture format used by Gamewave console") | ||
fmt.Println("Flags:") | ||
pflag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
failed := false | ||
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 file") | ||
usage() | ||
os.Exit(1) | ||
} | ||
|
||
for _, inputName := range args { | ||
f, err := os.Stat(inputName) | ||
if err != nil { | ||
fmt.Printf("Failed to get info about %s: %s", inputName, err) | ||
failed = true | ||
} | ||
if f.IsDir() { | ||
walkFunc := getWalkFunc(inputName) | ||
err := cwalk.Walk(inputName, walkFunc) | ||
if err != nil { | ||
fmt.Printf("Failed to unpack dir %s: %s\n", inputName, err) | ||
// for _, errors := range err.(cwalk.WalkerError).ErrorList { | ||
// fmt.Println(errors) | ||
// } | ||
failed = true | ||
} | ||
} else { | ||
if outputName == "" || len(args) > 1 { | ||
outputName = strings.TrimSuffix(inputName, filepath.Ext(inputName)) + ".zbm" | ||
} | ||
err := packTexture(inputName, outputName) | ||
if err != nil { | ||
fmt.Printf("Failed to unpack %s: %s", inputName, err) | ||
failed = true | ||
} | ||
} | ||
} | ||
if failed { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func getWalkFunc(basePath string) filepath.WalkFunc { | ||
return func(path string, info fs.FileInfo, _ error) error { | ||
if !info.IsDir() { | ||
// check if file is an image | ||
// file deepcode ignore PT: This is CLI tool, this is intended to be traversable | ||
file, err := os.Open(filepath.Join(basePath, path)) | ||
if err != nil { | ||
return err | ||
} | ||
_, format, err := image.DecodeConfig(file) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = file.Close() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err != nil && format != zbm.FormatName { | ||
outputName = filepath.Join(basePath, strings.TrimSuffix(path, filepath.Ext(path))+".zbm") | ||
return packTexture(filepath.Join(basePath, path), outputName) | ||
} | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
func packTexture(inputName, outputName string) error { | ||
// file deepcode ignore PT: This is CLI tool, this is intended to be traversable | ||
file, err := os.Open(inputName) | ||
if err != nil { | ||
return fmt.Errorf("couldn't open file %s: %s", inputName, err) | ||
} | ||
config, format, err := image.DecodeConfig(file) | ||
if err != nil { | ||
return fmt.Errorf("couldn't read image file config %s: %s", inputName, err) | ||
} | ||
|
||
fmt.Printf("Packing %s (detected %s): %dx%d\n", inputName, format, config.Width, config.Height) | ||
|
||
_, err = file.Seek(0, 0) | ||
if err != nil { | ||
return fmt.Errorf("couldn't seek in image file %s: %s", inputName, err) | ||
} | ||
|
||
img, _, err := image.Decode(file) | ||
if err != nil { | ||
return fmt.Errorf("couldn't read image file %s: %s", inputName, err) | ||
} | ||
|
||
err = file.Close() | ||
if err != nil { | ||
return fmt.Errorf("couldn't close image file %s: %s", inputName, err) | ||
} | ||
|
||
outputFile, err := os.Create(outputName) | ||
if err != nil { | ||
return fmt.Errorf("couldn't create output image file %s: %s", outputName, err) | ||
} | ||
|
||
err = zbm.Encode(outputFile, img) | ||
if err != nil { | ||
return fmt.Errorf("couldn't pack output image %s: %s", outputName, err) | ||
} | ||
|
||
err = outputFile.Close() | ||
if err != nil { | ||
return fmt.Errorf("couldn't close output image file %s: %s", outputName, 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
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
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,130 @@ | ||
package zbm | ||
|
||
import ( | ||
"encoding/binary" | ||
"image" | ||
"image/color" | ||
"io" | ||
"math" | ||
|
||
"github.com/namgo/GameWaveFans/pkg/common" | ||
) | ||
|
||
/* | ||
Currently, writer only outputs images in 3364CrCbYA image format, which all official games use | ||
*/ | ||
|
||
func convertColorToCrCbYA(c color.Color) uint16 { | ||
r, g, b, a := c.RGBA() | ||
col := uint16(0) | ||
|
||
r8 := uint8((float64(r) / 65536.0) * 255.0) | ||
g8 := uint8((float64(g) / 65536.0) * 255.0) | ||
b8 := uint8((float64(b) / 65536.0) * 255.0) | ||
|
||
y, cb, cr := color.RGBToYCbCr(r8, g8, b8) | ||
|
||
y6 := uint16(y >> 2) //uint16(math.Round((float64(y) / 255.0) * 63.0)) | ||
cr3 := uint16(cr >> 5) //uint16(math.Round((float64(cr) / 255.0) * 7.0)) | ||
cb3 := uint16(cb >> 5) //uint16(math.Round((float64(cb) / 255.0) * 7.0)) | ||
a4 := uint16(math.Round((float64(a) / 65536.0) * 15.0)) | ||
|
||
col = a4<<12 | y6<<6 | cb3<<3 | cr3 | ||
return col | ||
} | ||
|
||
func convertImage(m image.Image) []byte { | ||
pixelBuffer := make([]uint16, m.Bounds().Dx()*m.Bounds().Dy()) | ||
|
||
for y := 0; y < m.Bounds().Dy(); y++ { | ||
for x := 0; x < m.Bounds().Dx(); x++ { | ||
pixelBuffer[x+(y*m.Bounds().Dx())] = convertColorToCrCbYA(m.At(x, y)) | ||
} | ||
} | ||
|
||
data := make([]byte, len(pixelBuffer)*2) | ||
|
||
// swap every two pixels, endianness changes a bit | ||
for i := 0; i < len(pixelBuffer)-1; i += 2 { | ||
pixelData := make([]byte, 2) | ||
pixelData2 := make([]byte, 2) | ||
binary.BigEndian.PutUint16(pixelData, pixelBuffer[i]) | ||
binary.BigEndian.PutUint16(pixelData2, pixelBuffer[i+1]) | ||
|
||
data[i*2] = pixelData2[0] | ||
data[i*2+1] = pixelData2[1] | ||
data[i*2+2] = pixelData[0] | ||
data[i*2+3] = pixelData[1] | ||
} | ||
return data | ||
} | ||
|
||
// write header of zbm file, before packed data is written | ||
func writeHeader(w io.Writer, im image.Image, unpackedSize, packedSize uint32) error { | ||
if _, err := common.WriteUint32(w, 1); err != nil { | ||
return err | ||
} | ||
|
||
// TEXTURE_OSD | ||
if _, err := common.WriteUint32(w, 1); err != nil { | ||
return err | ||
} | ||
// 3364 format | ||
if _, err := common.WriteUint32(w, 4); err != nil { | ||
return err | ||
} | ||
// BPP | ||
if _, err := common.WriteUint32(w, 2); err != nil { | ||
return err | ||
} | ||
// width | ||
if _, err := common.WriteUint32(w, uint32(im.Bounds().Dx())); err != nil { | ||
return err | ||
} | ||
// height | ||
if _, err := common.WriteUint32(w, uint32(im.Bounds().Dy())); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := common.WriteUint32(w, 0); err != nil { | ||
return err | ||
} | ||
if _, err := common.WriteUint32(w, 0); err != nil { | ||
return err | ||
} | ||
if _, err := common.WriteUint32(w, 1); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := common.WriteUint32(w, packedSize); err != nil { | ||
return err | ||
} | ||
if _, err := common.WriteUint32(w, unpackedSize); err != nil { | ||
return err | ||
} | ||
|
||
_, err := common.WriteUint32(w, 0) | ||
return err | ||
} | ||
|
||
// Encode encodes image.Image to .zbm file | ||
func Encode(w io.Writer, m image.Image) error { | ||
//convert data | ||
convertedData := convertImage(m) | ||
|
||
// pack data | ||
packedData, err := common.WriteZlibToBuffer(convertedData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// write header | ||
err = writeHeader(w, m, uint32(len(convertedData)), uint32(len(packedData))) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// write data | ||
_, err = w.Write(packedData) | ||
return err | ||
} |