Adding modding folder with a hacked build.
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1/content/bitmap"
|
||||
"github.com/inkyblackness/hacked/ss1/content/movie/internal/compression"
|
||||
"github.com/inkyblackness/hacked/ss1/content/movie/internal/format"
|
||||
)
|
||||
|
||||
// Constants for video.
|
||||
const (
|
||||
HighResDefaultWidth = 600
|
||||
HighResDefaultHeight = 300
|
||||
)
|
||||
|
||||
// Video describes the visual part of a movie.
|
||||
type Video struct {
|
||||
// Width is the width of the video in pixel.
|
||||
Width uint16
|
||||
// Height is the height of the video in pixel.
|
||||
Height uint16
|
||||
// Scenes contain the frames of the video.
|
||||
Scenes []HighResScene
|
||||
}
|
||||
|
||||
// StartPalette returns the palette of the first scene. If no scene is present, a black palette is returned.
|
||||
func (video Video) StartPalette() bitmap.Palette {
|
||||
if len(video.Scenes) == 0 {
|
||||
return bitmap.Palette{}
|
||||
}
|
||||
return video.Scenes[0].palette
|
||||
}
|
||||
|
||||
func (video Video) duration() format.Timestamp {
|
||||
var sum format.Timestamp
|
||||
for _, scene := range video.Scenes {
|
||||
sum = sum.Plus(scene.duration())
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (video Video) encode() []format.EntryBucket {
|
||||
var sceneTime format.Timestamp
|
||||
var buckets []format.EntryBucket
|
||||
for index, scene := range video.Scenes {
|
||||
buckets = append(buckets, scene.encode(sceneTime, index != 0)...)
|
||||
sceneTime = sceneTime.Plus(scene.duration())
|
||||
}
|
||||
return buckets
|
||||
}
|
||||
|
||||
// Decompress unpacks all the frames of all scenes.
|
||||
func (video Video) Decompress() ([]Scene, error) {
|
||||
var scenes []Scene
|
||||
width := int(video.Width)
|
||||
height := int(video.Height)
|
||||
frameBuffer := make([]byte, width*height)
|
||||
decoderBuilder := compression.NewFrameDecoderBuilder(width, height)
|
||||
decoderBuilder.ForStandardFrame(frameBuffer, width)
|
||||
|
||||
cloneFramebuffer := func() []byte {
|
||||
bufferCopy := make([]byte, len(frameBuffer))
|
||||
copy(bufferCopy, frameBuffer)
|
||||
return bufferCopy
|
||||
}
|
||||
|
||||
for _, compressedScene := range video.Scenes {
|
||||
decoderBuilder.WithControlWords(compressedScene.controlWords)
|
||||
decoderBuilder.WithPaletteLookupList(compressedScene.paletteLookup)
|
||||
decoder := decoderBuilder.Build()
|
||||
var scene Scene
|
||||
scene.Palette = compressedScene.palette
|
||||
for _, compressedFrame := range compressedScene.frames {
|
||||
err := decoder.Decode(compressedFrame.bitstream, compressedFrame.maskstream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scene.Frames = append(scene.Frames, Frame{
|
||||
Pixels: cloneFramebuffer(),
|
||||
DisplayTime: compressedFrame.displayTime.ToDuration(),
|
||||
})
|
||||
}
|
||||
scenes = append(scenes, scene)
|
||||
}
|
||||
return scenes, nil
|
||||
}
|
||||
|
||||
// HighResScene is a set of frames with high-resolution compression.
|
||||
type HighResScene struct {
|
||||
palette bitmap.Palette
|
||||
paletteLookup []byte
|
||||
controlWords []compression.ControlWord
|
||||
frames []HighResFrame
|
||||
}
|
||||
|
||||
// HighResSceneFrom compresses given scene and returns the compression result.
|
||||
func HighResSceneFrom(ctx context.Context, scene Scene, width, height int) (HighResScene, error) {
|
||||
encoder := compression.NewSceneEncoder(width, height)
|
||||
for _, frame := range scene.Frames {
|
||||
err := encoder.AddFrame(frame.Pixels)
|
||||
if err != nil {
|
||||
return HighResScene{}, err
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
return HighResScene{}, ctx.Err()
|
||||
}
|
||||
}
|
||||
words, paletteLookup, frames, err := encoder.Encode(ctx)
|
||||
if err != nil {
|
||||
return HighResScene{}, err
|
||||
}
|
||||
compressedScene := HighResScene{
|
||||
palette: scene.Palette,
|
||||
paletteLookup: paletteLookup,
|
||||
controlWords: words,
|
||||
frames: make([]HighResFrame, len(frames)),
|
||||
}
|
||||
for index, frame := range frames {
|
||||
compressedScene.frames[index] = HighResFrame{
|
||||
bitstream: frame.Bitstream,
|
||||
maskstream: frame.Maskstream,
|
||||
displayTime: format.TimestampFromDuration(scene.Frames[index].DisplayTime),
|
||||
}
|
||||
}
|
||||
|
||||
return compressedScene, nil
|
||||
}
|
||||
|
||||
// WithFrameDisplayTime returns a new scene instance with the given display time set for all frames.
|
||||
func (scene HighResScene) WithFrameDisplayTime(displayTime time.Duration) HighResScene {
|
||||
newScene := scene
|
||||
newFrames := make([]HighResFrame, len(scene.frames))
|
||||
for index, frame := range scene.frames {
|
||||
newFrames[index] = frame.WithDisplayTime(displayTime)
|
||||
}
|
||||
newScene.frames = newFrames
|
||||
return newScene
|
||||
}
|
||||
|
||||
func (scene HighResScene) duration() format.Timestamp {
|
||||
var sum format.Timestamp
|
||||
for _, frame := range scene.frames {
|
||||
sum = sum.Plus(frame.duration())
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (scene HighResScene) encode(start format.Timestamp, withPalette bool) []format.EntryBucket {
|
||||
buckets := make([]format.EntryBucket, 0, len(scene.frames)+1)
|
||||
controlEntries := []format.Entry{
|
||||
{Timestamp: format.Timestamp{}, Data: format.PaletteLookupEntryData{List: scene.paletteLookup}},
|
||||
{Timestamp: format.Timestamp{}, Data: format.ControlDictionaryEntryData{Words: scene.controlWords}},
|
||||
}
|
||||
if withPalette {
|
||||
controlEntries = append(controlEntries,
|
||||
format.Entry{Timestamp: start, Data: format.PaletteResetEntryData{}},
|
||||
format.Entry{Timestamp: start, Data: format.PaletteEntryData{Colors: scene.palette}})
|
||||
}
|
||||
buckets = append(buckets,
|
||||
format.EntryBucket{
|
||||
Priority: format.EntryBucketPriorityVideoControl,
|
||||
Timestamp: start,
|
||||
Entries: controlEntries,
|
||||
})
|
||||
frameTime := start
|
||||
for _, frame := range scene.frames {
|
||||
buckets = append(buckets, frame.encode(frameTime))
|
||||
frameTime = frameTime.Plus(frame.displayTime)
|
||||
}
|
||||
|
||||
return buckets
|
||||
}
|
||||
|
||||
// HighResFrame contains the compressed information of a high-resolution picture in a scene.
|
||||
type HighResFrame struct {
|
||||
bitstream []byte
|
||||
maskstream []byte
|
||||
displayTime format.Timestamp
|
||||
}
|
||||
|
||||
// WithDisplayTime returns a new instance with the given display time set.
|
||||
func (frame HighResFrame) WithDisplayTime(displayTime time.Duration) HighResFrame {
|
||||
newFrame := frame
|
||||
newFrame.displayTime = format.TimestampFromDuration(displayTime)
|
||||
return newFrame
|
||||
}
|
||||
|
||||
func (frame HighResFrame) duration() format.Timestamp {
|
||||
return frame.displayTime
|
||||
}
|
||||
|
||||
func (frame HighResFrame) encode(start format.Timestamp) format.EntryBucket {
|
||||
return format.EntryBucket{
|
||||
Priority: format.EntryBucketPriorityFrame,
|
||||
Timestamp: start,
|
||||
Entries: []format.Entry{
|
||||
{
|
||||
Timestamp: start,
|
||||
Data: format.HighResVideoEntryData{
|
||||
Bitstream: frame.bitstream,
|
||||
Maskstream: frame.maskstream,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user