mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-23 10:07:30 +00:00
remove candidate, add limit frame, add multi game
This commit is contained in:
parent
81444a405b
commit
1048846266
22 changed files with 1214 additions and 1164 deletions
174
ui/director.go
174
ui/director.go
|
|
@ -1,87 +1,87 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
"github.com/giongto35/game-online/webrtc"
|
||||
)
|
||||
|
||||
type View interface {
|
||||
Enter()
|
||||
Exit()
|
||||
Update(t, dt float64)
|
||||
}
|
||||
|
||||
type Director struct {
|
||||
// audio *Audio
|
||||
view View
|
||||
timestamp float64
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
webRTC *webrtc.WebRTC
|
||||
}
|
||||
|
||||
func NewDirector(imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) *Director {
|
||||
// func NewDirector(audio *Audio, imageChannel chan *image.RGBA, inputChannel chan int) *Director {
|
||||
director := Director{}
|
||||
// director.audio = audio
|
||||
director.imageChannel = imageChannel
|
||||
director.inputChannel = inputChannel
|
||||
director.webRTC = webRTC
|
||||
return &director
|
||||
}
|
||||
|
||||
func (d *Director) SetView(view View) {
|
||||
if d.view != nil {
|
||||
d.view.Exit()
|
||||
}
|
||||
d.view = view
|
||||
if d.view != nil {
|
||||
d.view.Enter()
|
||||
}
|
||||
d.timestamp = float64(time.Now().Nanosecond()) / float64(time.Second)
|
||||
}
|
||||
|
||||
func (d *Director) Step() {
|
||||
//gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
timestamp := float64(time.Now().Nanosecond()) / float64(time.Second)
|
||||
dt := timestamp - d.timestamp
|
||||
d.timestamp = timestamp
|
||||
if d.view != nil {
|
||||
d.view.Update(timestamp, dt)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Director) Start(paths []string) {
|
||||
if len(paths) == 1 {
|
||||
d.PlayGame(paths[0])
|
||||
}
|
||||
d.Run()
|
||||
}
|
||||
|
||||
func (d *Director) Run() {
|
||||
for {
|
||||
// quit game
|
||||
if d.webRTC.IsClosed() {
|
||||
break
|
||||
}
|
||||
|
||||
d.Step()
|
||||
}
|
||||
d.SetView(nil)
|
||||
}
|
||||
|
||||
func (d *Director) PlayGame(path string) {
|
||||
hash, err := hashFile(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
console, err := nes.NewConsole(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
d.SetView(NewGameView(d, console, path, hash, d.imageChannel, d.inputChannel))
|
||||
}
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
"github.com/giongto35/game-online/webrtc"
|
||||
)
|
||||
|
||||
type View interface {
|
||||
Enter()
|
||||
Exit()
|
||||
Update(t, dt float64)
|
||||
}
|
||||
|
||||
type Director struct {
|
||||
// audio *Audio
|
||||
view View
|
||||
timestamp float64
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
webRTC *webrtc.WebRTC
|
||||
}
|
||||
|
||||
func NewDirector(imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) *Director {
|
||||
// func NewDirector(audio *Audio, imageChannel chan *image.RGBA, inputChannel chan int) *Director {
|
||||
director := Director{}
|
||||
// director.audio = audio
|
||||
director.imageChannel = imageChannel
|
||||
director.inputChannel = inputChannel
|
||||
director.webRTC = webRTC
|
||||
return &director
|
||||
}
|
||||
|
||||
func (d *Director) SetView(view View) {
|
||||
if d.view != nil {
|
||||
d.view.Exit()
|
||||
}
|
||||
d.view = view
|
||||
if d.view != nil {
|
||||
d.view.Enter()
|
||||
}
|
||||
d.timestamp = float64(time.Now().Nanosecond()) / float64(time.Second)
|
||||
}
|
||||
|
||||
func (d *Director) Step() {
|
||||
//gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
timestamp := float64(time.Now().Nanosecond()) / float64(time.Second)
|
||||
dt := timestamp - d.timestamp
|
||||
d.timestamp = timestamp
|
||||
if d.view != nil {
|
||||
d.view.Update(timestamp, dt)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Director) Start(paths []string) {
|
||||
if len(paths) == 1 {
|
||||
d.PlayGame(paths[0])
|
||||
}
|
||||
d.Run()
|
||||
}
|
||||
|
||||
func (d *Director) Run() {
|
||||
for {
|
||||
// quit game
|
||||
if d.webRTC.IsClosed() {
|
||||
break
|
||||
}
|
||||
|
||||
d.Step()
|
||||
}
|
||||
d.SetView(nil)
|
||||
}
|
||||
|
||||
func (d *Director) PlayGame(path string) {
|
||||
hash, err := hashFile(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
console, err := nes.NewConsole(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
d.SetView(NewGameView(d, console, path, hash, d.imageChannel, d.inputChannel))
|
||||
}
|
||||
|
|
|
|||
223
ui/gameview.go
223
ui/gameview.go
|
|
@ -1,106 +1,117 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"fmt"
|
||||
// "strconv"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
)
|
||||
|
||||
const padding = 0
|
||||
|
||||
type GameView struct {
|
||||
director *Director
|
||||
console *nes.Console
|
||||
title string
|
||||
hash string
|
||||
record bool
|
||||
frames []image.Image
|
||||
|
||||
keyPressed [8]bool
|
||||
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
}
|
||||
|
||||
func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA, inputChannel chan int) View {
|
||||
gameview := &GameView{director, console, title, hash, false, nil, [8]bool{false}, imageChannel, inputChannel}
|
||||
go gameview.ListenToInputChannel()
|
||||
return gameview
|
||||
}
|
||||
|
||||
func (view *GameView) ListenToInputChannel() {
|
||||
for {
|
||||
key := <-view.inputChannel
|
||||
s := fmt.Sprintf("%.8b", key)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '1' {
|
||||
view.keyPressed[i] = true
|
||||
} else {
|
||||
view.keyPressed[i] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) Enter() {
|
||||
// view.console.SetAudioChannel(view.director.audio.channel)
|
||||
// view.console.SetAudioSampleRate(view.director.audio.sampleRate)
|
||||
// load state
|
||||
if err := view.console.LoadState(savePath(view.hash)); err == nil {
|
||||
return
|
||||
} else {
|
||||
view.console.Reset()
|
||||
}
|
||||
// load sram
|
||||
cartridge := view.console.Cartridge
|
||||
if cartridge.Battery != 0 {
|
||||
if sram, err := readSRAM(sramPath(view.hash)); err == nil {
|
||||
cartridge.SRAM = sram
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) Exit() {
|
||||
// view.console.SetAudioChannel(nil)
|
||||
// view.console.SetAudioSampleRate(0)
|
||||
// save sram
|
||||
cartridge := view.console.Cartridge
|
||||
if cartridge.Battery != 0 {
|
||||
writeSRAM(sramPath(view.hash), cartridge.SRAM)
|
||||
}
|
||||
// save state
|
||||
view.console.SaveState(savePath(view.hash))
|
||||
}
|
||||
|
||||
func (view *GameView) Update(t, dt float64) {
|
||||
if dt > 1 {
|
||||
dt = 0
|
||||
}
|
||||
console := view.console
|
||||
//updateControllers(window, console)
|
||||
view.updateControllers()
|
||||
//fmt.Println(console.Buffer())
|
||||
console.StepSeconds(dt)
|
||||
view.imageChannel <- console.Buffer()
|
||||
if view.record {
|
||||
view.frames = append(view.frames, copyImage(console.Buffer()))
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) updateControllers() {
|
||||
// TODO: switch case
|
||||
// var buttons [8]bool
|
||||
// buttons[nes.ButtonLeft] = view.keyPressed[37]
|
||||
// buttons[nes.ButtonUp] = view.keyPressed[38]
|
||||
// buttons[nes.ButtonRight] = view.keyPressed[39]
|
||||
// buttons[nes.ButtonDown] = view.keyPressed[40]
|
||||
// buttons[nes.ButtonA] = view.keyPressed[32]
|
||||
// buttons[nes.ButtonB] = view.keyPressed[17]
|
||||
// buttons[nes.ButtonStart] = view.keyPressed[13]
|
||||
// buttons[nes.ButtonSelect] = view.keyPressed[16]
|
||||
// view.console.Controller1.SetButtons(buttons)
|
||||
view.console.Controller1.SetButtons(view.keyPressed)
|
||||
}
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
)
|
||||
|
||||
const padding = 0
|
||||
|
||||
type GameView struct {
|
||||
director *Director
|
||||
console *nes.Console
|
||||
title string
|
||||
hash string
|
||||
record bool
|
||||
frames []image.Image
|
||||
|
||||
keyPressed [8]bool
|
||||
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
|
||||
nanotime int64
|
||||
}
|
||||
|
||||
func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA, inputChannel chan int) View {
|
||||
gameview := &GameView{director, console, title, hash, false, nil, [8]bool{false}, imageChannel, inputChannel, time.Now().UnixNano()}
|
||||
go gameview.ListenToInputChannel()
|
||||
return gameview
|
||||
}
|
||||
|
||||
func (view *GameView) ListenToInputChannel() {
|
||||
for {
|
||||
key := <-view.inputChannel
|
||||
s := fmt.Sprintf("%.8b", key)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '1' {
|
||||
view.keyPressed[i] = true
|
||||
} else {
|
||||
view.keyPressed[i] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) Enter() {
|
||||
// view.console.SetAudioChannel(view.director.audio.channel)
|
||||
// view.console.SetAudioSampleRate(view.director.audio.sampleRate)
|
||||
// load state
|
||||
if err := view.console.LoadState(savePath(view.hash)); err == nil {
|
||||
return
|
||||
} else {
|
||||
view.console.Reset()
|
||||
}
|
||||
// load sram
|
||||
cartridge := view.console.Cartridge
|
||||
if cartridge.Battery != 0 {
|
||||
if sram, err := readSRAM(sramPath(view.hash)); err == nil {
|
||||
cartridge.SRAM = sram
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) Exit() {
|
||||
// view.console.SetAudioChannel(nil)
|
||||
// view.console.SetAudioSampleRate(0)
|
||||
// save sram
|
||||
cartridge := view.console.Cartridge
|
||||
if cartridge.Battery != 0 {
|
||||
writeSRAM(sramPath(view.hash), cartridge.SRAM)
|
||||
}
|
||||
// save state
|
||||
view.console.SaveState(savePath(view.hash))
|
||||
}
|
||||
|
||||
func (view *GameView) Update(t, dt float64) {
|
||||
if dt > 1 {
|
||||
dt = 0
|
||||
}
|
||||
console := view.console
|
||||
//updateControllers(window, console)
|
||||
view.updateControllers()
|
||||
//fmt.Println(console.Buffer())
|
||||
console.StepSeconds(dt)
|
||||
|
||||
// fps to set frame
|
||||
n := time.Now().UnixNano()
|
||||
if n - view.nanotime > 1000000000 / 100000 {
|
||||
view.nanotime = n
|
||||
view.imageChannel <- console.Buffer()
|
||||
}
|
||||
|
||||
|
||||
|
||||
if view.record {
|
||||
view.frames = append(view.frames, copyImage(console.Buffer()))
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) updateControllers() {
|
||||
// TODO: switch case
|
||||
// var buttons [8]bool
|
||||
// buttons[nes.ButtonLeft] = view.keyPressed[37]
|
||||
// buttons[nes.ButtonUp] = view.keyPressed[38]
|
||||
// buttons[nes.ButtonRight] = view.keyPressed[39]
|
||||
// buttons[nes.ButtonDown] = view.keyPressed[40]
|
||||
// buttons[nes.ButtonA] = view.keyPressed[32]
|
||||
// buttons[nes.ButtonB] = view.keyPressed[17]
|
||||
// buttons[nes.ButtonStart] = view.keyPressed[13]
|
||||
// buttons[nes.ButtonSelect] = view.keyPressed[16]
|
||||
// view.console.Controller1.SetButtons(buttons)
|
||||
view.console.Controller1.SetButtons(view.keyPressed)
|
||||
}
|
||||
|
|
|
|||
84
ui/run.go
84
ui/run.go
|
|
@ -1,42 +1,42 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
// "log"
|
||||
"runtime"
|
||||
|
||||
// "github.com/gordonklaus/portaudio"
|
||||
"github.com/giongto35/game-online/webrtc"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 256
|
||||
height = 240
|
||||
scale = 3
|
||||
title = "NES"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// we need a parallel OS thread to avoid audio stuttering
|
||||
runtime.GOMAXPROCS(2)
|
||||
|
||||
// we need to keep OpenGL calls on a single thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) {
|
||||
// initialize audio
|
||||
// portaudio.Initialize()
|
||||
// defer portaudio.Terminate()
|
||||
|
||||
// audio := NewAudio()
|
||||
// if err := audio.Start(); err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
// defer audio.Stop()
|
||||
|
||||
// run director
|
||||
director := NewDirector(imageChannel, inputChannel, webRTC)
|
||||
// director := NewDirector(audio, imageChannel, inputChannel)
|
||||
director.Start(paths)
|
||||
}
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
// "log"
|
||||
"runtime"
|
||||
|
||||
// "github.com/gordonklaus/portaudio"
|
||||
"github.com/giongto35/game-online/webrtc"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 256
|
||||
height = 240
|
||||
scale = 3
|
||||
title = "NES"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// we need a parallel OS thread to avoid audio stuttering
|
||||
// runtime.GOMAXPROCS(2)
|
||||
|
||||
// we need to keep OpenGL calls on a single thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int, webRTC *webrtc.WebRTC) {
|
||||
// initialize audio
|
||||
// portaudio.Initialize()
|
||||
// defer portaudio.Terminate()
|
||||
|
||||
// audio := NewAudio()
|
||||
// if err := audio.Start(); err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
// defer audio.Stop()
|
||||
|
||||
// run director
|
||||
director := NewDirector(imageChannel, inputChannel, webRTC)
|
||||
// director := NewDirector(audio, imageChannel, inputChannel)
|
||||
director.Start(paths)
|
||||
}
|
||||
|
|
|
|||
306
ui/util.go
306
ui/util.go
|
|
@ -1,153 +1,153 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/gif"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
)
|
||||
|
||||
var homeDir string
|
||||
|
||||
func init() {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
homeDir = u.HomeDir
|
||||
}
|
||||
|
||||
func thumbnailURL(hash string) string {
|
||||
return "http://www.michaelfogleman.com/static/nes/" + hash + ".png"
|
||||
}
|
||||
|
||||
func thumbnailPath(hash string) string {
|
||||
return homeDir + "/.nes/thumbnail/" + hash + ".png"
|
||||
}
|
||||
|
||||
func sramPath(hash string) string {
|
||||
return homeDir + "/.nes/sram/" + hash + ".dat"
|
||||
}
|
||||
|
||||
func savePath(hash string) string {
|
||||
return homeDir + "/.nes/save/" + hash + ".dat"
|
||||
}
|
||||
|
||||
func combineButtons(a, b [8]bool) [8]bool {
|
||||
var result [8]bool
|
||||
for i := 0; i < 8; i++ {
|
||||
result[i] = a[i] || b[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func hashFile(path string) (string, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", md5.Sum(data)), nil
|
||||
}
|
||||
func copyImage(src image.Image) *image.RGBA {
|
||||
dst := image.NewRGBA(src.Bounds())
|
||||
draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func loadPNG(path string) (image.Image, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return png.Decode(file)
|
||||
}
|
||||
|
||||
func savePNG(path string, im image.Image) error {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return png.Encode(file, im)
|
||||
}
|
||||
|
||||
func saveGIF(path string, frames []image.Image) error {
|
||||
var palette []color.Color
|
||||
for _, c := range nes.Palette {
|
||||
palette = append(palette, c)
|
||||
}
|
||||
g := gif.GIF{}
|
||||
for i, src := range frames {
|
||||
if i%3 != 0 {
|
||||
continue
|
||||
}
|
||||
dst := image.NewPaletted(src.Bounds(), palette)
|
||||
draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
|
||||
g.Image = append(g.Image, dst)
|
||||
g.Delay = append(g.Delay, 5)
|
||||
}
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return gif.EncodeAll(file, &g)
|
||||
}
|
||||
|
||||
func screenshot(im image.Image) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
path := fmt.Sprintf("%03d.png", i)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
savePNG(path, im)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animation(frames []image.Image) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
path := fmt.Sprintf("%03d.gif", i)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
saveGIF(path, frames)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSRAM(filename string, sram []byte) error {
|
||||
dir, _ := path.Split(filename)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return binary.Write(file, binary.LittleEndian, sram)
|
||||
}
|
||||
|
||||
func readSRAM(filename string) ([]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
sram := make([]byte, 0x2000)
|
||||
if err := binary.Read(file, binary.LittleEndian, sram); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sram, nil
|
||||
}
|
||||
package ui
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/gif"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/giongto35/game-online/nes"
|
||||
)
|
||||
|
||||
var homeDir string
|
||||
|
||||
func init() {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
homeDir = u.HomeDir
|
||||
}
|
||||
|
||||
func thumbnailURL(hash string) string {
|
||||
return "http://www.michaelfogleman.com/static/nes/" + hash + ".png"
|
||||
}
|
||||
|
||||
func thumbnailPath(hash string) string {
|
||||
return homeDir + "/.nes/thumbnail/" + hash + ".png"
|
||||
}
|
||||
|
||||
func sramPath(hash string) string {
|
||||
return homeDir + "/.nes/sram/" + hash + ".dat"
|
||||
}
|
||||
|
||||
func savePath(hash string) string {
|
||||
return homeDir + "/.nes/save/" + hash + ".dat"
|
||||
}
|
||||
|
||||
func combineButtons(a, b [8]bool) [8]bool {
|
||||
var result [8]bool
|
||||
for i := 0; i < 8; i++ {
|
||||
result[i] = a[i] || b[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func hashFile(path string) (string, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", md5.Sum(data)), nil
|
||||
}
|
||||
func copyImage(src image.Image) *image.RGBA {
|
||||
dst := image.NewRGBA(src.Bounds())
|
||||
draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func loadPNG(path string) (image.Image, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return png.Decode(file)
|
||||
}
|
||||
|
||||
func savePNG(path string, im image.Image) error {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return png.Encode(file, im)
|
||||
}
|
||||
|
||||
func saveGIF(path string, frames []image.Image) error {
|
||||
var palette []color.Color
|
||||
for _, c := range nes.Palette {
|
||||
palette = append(palette, c)
|
||||
}
|
||||
g := gif.GIF{}
|
||||
for i, src := range frames {
|
||||
if i%3 != 0 {
|
||||
continue
|
||||
}
|
||||
dst := image.NewPaletted(src.Bounds(), palette)
|
||||
draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
|
||||
g.Image = append(g.Image, dst)
|
||||
g.Delay = append(g.Delay, 5)
|
||||
}
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return gif.EncodeAll(file, &g)
|
||||
}
|
||||
|
||||
func screenshot(im image.Image) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
path := fmt.Sprintf("%03d.png", i)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
savePNG(path, im)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animation(frames []image.Image) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
path := fmt.Sprintf("%03d.gif", i)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
saveGIF(path, frames)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSRAM(filename string, sram []byte) error {
|
||||
dir, _ := path.Split(filename)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return binary.Write(file, binary.LittleEndian, sram)
|
||||
}
|
||||
|
||||
func readSRAM(filename string) ([]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
sram := make([]byte, 0x2000)
|
||||
if err := binary.Read(file, binary.LittleEndian, sram); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sram, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue