commit 023563f7139715c97e3cb62c62fe417719e57904 Author: elee Date: Fri Mar 25 00:14:03 2022 -0500 file reading diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b9c917 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +bin/ +out/ +dist/ + + +*.exe +*.out +*.bin + +*test.png diff --git a/common/nori/animation.go b/common/nori/animation.go new file mode 100644 index 0000000..d9bafb7 --- /dev/null +++ b/common/nori/animation.go @@ -0,0 +1,139 @@ +package nori + +import ( + "bufio" + "encoding/binary" + "io" +) + +type Frame struct { + Duration uint32 + PlaneCount uint32 + Planes []*Plane + CoordinateCount uint32 + Coordinates [][2]uint32 + Unknown1 string + Sound_effect string + Unknown2 string + Camp CampSection +} +type CampSection struct { + Params [7]uint32 + Array []byte + Extra string +} + +type Animation struct { + Title string + + // std::vector frame_offsets + FrameCount uint32 + Frames []*Frame +} + +func (a *Animation) Decode(rd *bufio.Reader, version int) error { + if err := binary.Read(rd, end, &a.FrameCount); err != nil { + return err + } + a.Frames = make([]*Frame, int(a.FrameCount)) + for i := range a.Frames { + cf := &Frame{} + a.Frames[i] = cf + if err := binary.Read(rd, end, &cf.Duration); err != nil { + return err + } + + if err := binary.Read(rd, end, &cf.PlaneCount); err != nil { + return err + } + + cf.Planes = make([]*Plane, int(cf.PlaneCount)) + for i := range cf.Planes { + cp := &Plane{} + cf.Planes[i] = cp + if err := binary.Read(rd, end, &cp.PlaneX); err != nil { + return err + } + + if err := binary.Read(rd, end, &cp.PlaneY); err != nil { + return err + } + + if err := binary.Read(rd, end, &cp.Opacity); err != nil { + return err + } + if err := binary.Read(rd, end, &cp.RenderFlag); err != nil { + return err + } + if err := binary.Read(rd, end, &cp.FlagParam); err != nil { + return err + } + } + if version != 300 { + if err := binary.Read(rd, end, &cf.CoordinateCount); err != nil { + return err + } + cf.Coordinates = make([][2]uint32, cf.CoordinateCount) + for i := range cf.Coordinates { + if err := binary.Read(rd, end, cf.Coordinates[i]); err != nil { + return err + } + } + } + switch version { + case 300, 301: + if _, err := rd.Discard(144); err != nil { + return err + } + case 302, 303: + if _, err := rd.Discard(96); err != nil { + return err + } + } + if version >= 302 { + // entry blocks + if _, err := rd.Discard(28 * 6); err != nil { + return err + } + } + // unknown1 + if _, err := rd.Discard(44); err != nil { + return err + } + //sound_effect + if _, err := rd.Discard(18); err != nil { + return err + } + // unknown2 + if _, err := rd.Discard(18); err != nil { + return err + } + var cast uint32 + + if err := binary.Read(rd, end, &cast); err != nil { + return err + } + if version > 303 && (cast > 1) { + cf.Camp = CampSection{} + if err := binary.Read(rd, end, &cf.Camp.Params); err != nil { + return err + } + sz := int(cf.Camp.Params[1]) * int(cf.Camp.Params[2]) + cf.Camp.Array = make([]byte, sz) + if _, err := io.ReadFull(rd, cf.Camp.Array); err != nil { + return err + } + } + } + return nil +} + +type Plane struct { + Bitmap_id uint32 + PlaneX int32 + PlaneY int32 + Opacity uint32 + Blend BlendMode + FlagParam uint32 + RenderFlag uint32 +} diff --git a/common/nori/blend.go b/common/nori/blend.go new file mode 100644 index 0000000..d5188ea --- /dev/null +++ b/common/nori/blend.go @@ -0,0 +1,18 @@ +package nori + +type BlendMode uint32 + +var ( + BlendMode_None BlendMode = 0 + BlendMode_Alpha BlendMode = 1 + // Blend_ALPHA + BlendMode_InvertMul BlendMode = 2 + // Color_Invert | Blend_MUL + BlendMode_Unknown3 BlendMode = 3 + BlendMode_Add BlendMode = 4 + BlendMode_InvertMul5 BlendMode = 5 + // Color_Invert | Blend_MUL + BlendMode_Mul BlendMode = 6 + BlendMode_Mul7 BlendMode = 7 + BlendMode_Add8 BlendMode = 8 +) diff --git a/common/nori/image.go b/common/nori/image.go new file mode 100644 index 0000000..b5b6a20 --- /dev/null +++ b/common/nori/image.go @@ -0,0 +1,71 @@ +package nori + +import ( + "bufio" + "encoding/binary" + "fmt" + "image" + "image/color" + "io" +) + +type Image struct { + Bpp uint32 + Count uint32 + Delay uint32 + OffsetX int32 + OffsetY int32 + + Width uint32 + Height uint32 + + Size uint32 + Data []byte + + Frame image.Image +} + +func (i *Image) Decode(rd *bufio.Reader) error { + cf := image.NewRGBA64(image.Rect(0, 0, int(i.Width), int(i.Height))) + i.Frame = cf + switch i.Bpp { + case 8: + rd.Discard(int(i.Size)) + return fmt.Errorf("bpp = 8, but no palette") + case 16: + var bgr uint16 + for idx := 0; idx < int(i.Size/2); idx++ { + if err := binary.Read(rd, end, &bgr); err != nil { + return err + } + red := uint8(((0x7c00 & bgr) >> 10) * 255 / 31) + green := uint8(((0x3e0 & bgr) >> 5) * 255 / 31) + blue := uint8((0x1f & bgr) * 255 / 31) + if red != 0 && blue != 0 && green != 0 { + } + col := &color.RGBA{ + red, green, blue, + 0xff, + } + cf.Set(idx%int(i.Width), idx/int(i.Width), col) + } + return nil + case 24: + i.Data = make([]byte, i.Size) + for idx := 0; idx < int(i.Size/3); idx++ { + rgb := [3]byte{} + _, err := io.ReadFull(rd, rgb[:]) + if err != nil { + return err + } + col := &color.RGBA{ + rgb[0], rgb[1], rgb[2], + 0xff, + } + cf.Set(idx%int(i.Width), idx/int(i.Width), col) + } + return nil + default: + return fmt.Errorf("unsupported bpp, %d", i.Bpp) + } +} diff --git a/common/nori/nori.go b/common/nori/nori.go new file mode 100644 index 0000000..4544e1c --- /dev/null +++ b/common/nori/nori.go @@ -0,0 +1,40 @@ +package nori + +type PaletteSection struct { + Version uint32 + Param01 uint32 + Param02 uint32 + Param03 uint32 + Param04 uint32 + Divided bool + DataLength uint32 + // Graphics::Palette color_data +} + +type GawiSection struct { + Version uint32 + Bpp uint32 + IsCompressed bool + Params [4]uint32 + BmpCount uint32 + Size uint32 + + HasPalette bool + Palette *PaletteSection + // std::vector bmp_offsets + Images []*Image +} + +type Nori struct { + Version uint32 + Params [5]uint32 + AnimationCount uint32 + SizeNoGawi uint32 + TotalSize uint32 + + Gawi *GawiSection + Animations []*Animation + AnimationByKey map[string]*Animation + + lastSignature [4]byte +} diff --git a/common/nori/nori_test.go b/common/nori/nori_test.go new file mode 100644 index 0000000..f18e194 --- /dev/null +++ b/common/nori/nori_test.go @@ -0,0 +1,30 @@ +package nori + +import ( + "os" + "testing" +) + +func TestParseFile1(t *testing.T) { + rd, err := os.OpenFile("./nori_test/test1.nri", os.O_RDONLY, 0666) + if err != nil { + t.Fatalf("open file: %s", err) + } + nori, err := NewReader(rd).Decode() + if err != nil { + t.Fatalf("decode: %s", err) + } + t.Logf("\n nori: %+v\n gawi: %+v", nori, nori.Gawi) +} + +func TestParseFile2(t *testing.T) { + rd, err := os.OpenFile("./nori_test/test2.nri", os.O_RDONLY, 0666) + if err != nil { + t.Fatalf("open file: %s", err) + } + nori, err := NewReader(rd).Decode() + if err != nil { + t.Fatalf("decode: %s", err) + } + t.Logf("\n nori: %+v\n gawi: %+v", nori, nori.Gawi) +} diff --git a/common/nori/nori_test/test1.nri b/common/nori/nori_test/test1.nri new file mode 100644 index 0000000..93e2ee3 Binary files /dev/null and b/common/nori/nori_test/test1.nri differ diff --git a/common/nori/nori_test/test2.nri b/common/nori/nori_test/test2.nri new file mode 100644 index 0000000..aa8f861 Binary files /dev/null and b/common/nori/nori_test/test2.nri differ diff --git a/common/nori/reader.go b/common/nori/reader.go new file mode 100644 index 0000000..23a1deb --- /dev/null +++ b/common/nori/reader.go @@ -0,0 +1,165 @@ +package nori + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" +) + +var end = binary.LittleEndian + +type Reader struct { + nori *Nori + + r *bufio.Reader +} + +func NewReader(rd io.Reader) *Reader { + o := &Reader{ + nori: &Nori{}, + r: bufio.NewReader(rd), + } + return o +} + +func (n *Reader) Decode() (*Nori, error) { + if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil { + return nil, err + } + if sig, target := string(n.nori.lastSignature[:]), "NORI"; sig != target { + return nil, fmt.Errorf("expected header %s, got %s", target, sig) + } + if err := binary.Read(n, end, &n.nori.Version); err != nil { + return nil, err + } + if err := binary.Read(n, end, n.nori.Params[:]); err != nil { + return nil, err + } + if err := binary.Read(n, end, &n.nori.AnimationCount); err != nil { + return nil, err + } + if err := binary.Read(n, end, &n.nori.SizeNoGawi); err != nil { + return nil, err + } + if err := binary.Read(n, end, &n.nori.TotalSize); err != nil { + return nil, err + } + if err := n.decodeGawi(); err != nil { + return nil, err + } + return n.nori, nil +} + +func (n *Reader) decodeGawi() error { + g := &GawiSection{} + n.nori.Gawi = g + if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil { + return err + } + if sig, target := string(n.nori.lastSignature[:]), "GAWI"; sig != target { + return fmt.Errorf("bad sig: want %s, got %s", target, sig) + } + if err := binary.Read(n, end, &g.Version); err != nil { + return err + } + if g.Version != 300 { + return fmt.Errorf("bad ver: want 300, got %d", g.Version) + } + if err := binary.Read(n, end, &g.Bpp); err != nil { + return err + } + good := false + for _, v := range [3]uint32{8, 16, 24} { + if g.Bpp == v { + good = true + } + } + if !good { + return fmt.Errorf("bad bpp: %d", g.Bpp) + } + var container uint32 + if err := binary.Read(n, end, &container); err != nil { + return err + } + g.IsCompressed = (container == 1) + if err := binary.Read(n, end, &container); err != nil { + return err + } + g.HasPalette = (container > 0) + + if err := binary.Read(n, end, g.Params[:]); err != nil { + return err + } + if err := binary.Read(n, end, &g.BmpCount); err != nil { + return err + } + if err := binary.Read(n, end, &g.Size); err != nil { + return err + } + if g.HasPalette { + n.decodePalette() + } + if _, err := n.r.Discard(4 * int(g.BmpCount)); err != nil { + return err + } + g.Images = make([]*Image, int(g.BmpCount)) + for i := range g.Images { + g.Images[i] = &Image{Bpp: g.Bpp} + if err := n.decodeImage(g.Images[i]); err != nil { + return err + } + } + if _, err := n.r.Discard(4 * int(n.nori.AnimationCount)); err != nil { + return err + } + n.nori.Animations = make([]*Animation, n.nori.AnimationCount) + return nil +} + +func (n *Reader) decodePalette() error { + if sig, target := string(n.nori.lastSignature[:]), "PAL_"; sig != target { + return fmt.Errorf("bad sig: want %s, got %s", target, sig) + } + return nil +} +func (n *Reader) decodeImage(img *Image) error { + if err := binary.Read(n, end, &img.Count); err != nil { + return err + } + if img.Count != 1 { + return fmt.Errorf("img count should be 1") + } + + if err := binary.Read(n, end, &img.Size); err != nil { + return err + } + + if err := binary.Read(n, end, &img.Width); err != nil { + return err + } + + if err := binary.Read(n, end, &img.Height); err != nil { + return err + } + + if err := binary.Read(n, end, &img.Delay); err != nil { + return err + } + + if err := binary.Read(n, end, &img.OffsetX); err != nil { + return err + } + + if err := binary.Read(n, end, &img.OffsetY); err != nil { + return err + } + if err := img.Decode(n.r); err != nil { + return err + } + return nil +} + +func (n *Reader) Read(b []byte) (int, error) { + return n.r.Read(b) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f62b7f0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.tuxpa.in/a/gotagonist + +go 1.18