Adding modding folder with a hacked build.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
package compression
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1"
|
||||
)
|
||||
|
||||
const (
|
||||
errTooHighOperationOffset ss1.StringError = "too high operation offset"
|
||||
)
|
||||
|
||||
// TileColorOp describes one operation how a tile should be colored.
|
||||
type TileColorOp struct {
|
||||
Type ControlType
|
||||
Offset uint32
|
||||
}
|
||||
|
||||
// ControlWordSequencer receives a list of requested tile coloring operations
|
||||
// and produces a sequence of low-level control words to reproduce the requested list.
|
||||
type ControlWordSequencer struct {
|
||||
// BitstreamIndexLimit specifies the highest value the sequencer may use to store offset values in the bitstream.
|
||||
// If this value is 0, then the default of 0xFFF (12 bits) is used.
|
||||
BitstreamIndexLimit uint32
|
||||
|
||||
// DirectIndexLimit specifies the amount of direct pointers into the bitstream.
|
||||
// If this value is 0, then the default of BitstreamIndexLimit / 4 is used.
|
||||
DirectIndexLimit uint32
|
||||
|
||||
ops map[TileColorOp]uint32
|
||||
}
|
||||
|
||||
// Add extends the list of requested coloring operations with the given entry.
|
||||
// The offset of the entry must be a value less than or equal ControlWordParamLimit, otherwise the function returns an error.
|
||||
func (seq *ControlWordSequencer) Add(op TileColorOp) error {
|
||||
if op.Offset > ControlWordParamLimit {
|
||||
return errTooHighOperationOffset
|
||||
}
|
||||
if seq.ops == nil {
|
||||
seq.ops = make(map[TileColorOp]uint32)
|
||||
}
|
||||
seq.ops[op]++
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sequence packs the requested list of entries into a sequence of control words.
|
||||
// An error is returned if the operations would exceed the possible storage space.
|
||||
func (seq ControlWordSequencer) Sequence() (ControlWordSequence, error) {
|
||||
var result ControlWordSequence
|
||||
var sortedOps []TileColorOp
|
||||
for op := range seq.ops {
|
||||
sortedOps = append(sortedOps, op)
|
||||
}
|
||||
sort.Slice(sortedOps, func(a, b int) bool {
|
||||
opA := sortedOps[a]
|
||||
opB := sortedOps[b]
|
||||
countA := seq.ops[opA]
|
||||
countB := seq.ops[opB]
|
||||
if countA != countB {
|
||||
return countA > countB
|
||||
}
|
||||
if opA.Offset != opB.Offset {
|
||||
return opA.Offset < opB.Offset
|
||||
}
|
||||
if opA.Type != opB.Type {
|
||||
return opA.Type < opB.Type
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
bitstreamIndexLimit := seq.BitstreamIndexLimit
|
||||
if bitstreamIndexLimit == 0 {
|
||||
bitstreamIndexLimit = 0xFFF
|
||||
}
|
||||
maximumDirect := seq.DirectIndexLimit
|
||||
if maximumDirect > bitstreamIndexLimit {
|
||||
maximumDirect = bitstreamIndexLimit
|
||||
} else if maximumDirect == 0 {
|
||||
maximumDirect = bitstreamIndexLimit / 4
|
||||
}
|
||||
requiredExtensions := uint32(0)
|
||||
|
||||
totalOps := uint32(len(sortedOps))
|
||||
if totalOps > maximumDirect {
|
||||
requiredExtensions = (totalOps - maximumDirect + 15) / 16
|
||||
if requiredExtensions > (bitstreamIndexLimit - maximumDirect) {
|
||||
return result, tooManyWordsError{
|
||||
Total: totalOps,
|
||||
MaximumDirect: maximumDirect,
|
||||
BitstreamIndexLimit: bitstreamIndexLimit,
|
||||
}
|
||||
}
|
||||
}
|
||||
extensionStart := maximumDirect + requiredExtensions
|
||||
|
||||
result.opPaths = make(map[TileColorOp]nestedTileColorOp)
|
||||
for opIndex, op := range sortedOps {
|
||||
if uint32(opIndex) == maximumDirect {
|
||||
// write required amount of long offset entries
|
||||
for i := uint32(0); i < requiredExtensions; i++ {
|
||||
result.words = append(result.words, LongOffsetOf(extensionStart+i*16))
|
||||
}
|
||||
}
|
||||
if uint32(opIndex) >= maximumDirect {
|
||||
offset := uint32(opIndex) - maximumDirect
|
||||
result.opPaths[op] = nestedTileColorOp{
|
||||
parent: &nestedTileColorOp{relOffsetBits: 12, relOffset: maximumDirect + offset/16},
|
||||
relOffsetBits: 4,
|
||||
relOffset: offset % 16,
|
||||
}
|
||||
result.words = append(result.words, ControlWordOf(4, op.Type, op.Offset))
|
||||
} else {
|
||||
result.opPaths[op] = nestedTileColorOp{parent: nil, relOffsetBits: 12, relOffset: uint32(opIndex)}
|
||||
result.words = append(result.words, ControlWordOf(12, op.Type, op.Offset))
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type nestedTileColorOp struct {
|
||||
parent *nestedTileColorOp
|
||||
relOffsetBits uint
|
||||
relOffset uint32
|
||||
}
|
||||
|
||||
func (op nestedTileColorOp) writeTo(w *BitstreamWriter) {
|
||||
if op.parent != nil {
|
||||
op.parent.writeTo(w)
|
||||
}
|
||||
w.Write(op.relOffsetBits, op.relOffset)
|
||||
}
|
||||
|
||||
// ControlWordSequence is a finalized set of control words to reproduce a list of
|
||||
// tile coloring operations. Based on this sequence, a bitstream can be created based
|
||||
// on a selection of such coloring operations (i.e., per frame).
|
||||
type ControlWordSequence struct {
|
||||
// HTiles specifies the amount of horizontal tiles (= operations) a frame has.
|
||||
// This number is relevant for skip operations. If 0, then no compression of skip-operations is done.
|
||||
HTiles uint32
|
||||
|
||||
words []ControlWord
|
||||
opPaths map[TileColorOp]nestedTileColorOp
|
||||
}
|
||||
|
||||
// ControlWords returns the list of low-level control words of the sequence.
|
||||
func (seq ControlWordSequence) ControlWords() []ControlWord {
|
||||
return seq.words
|
||||
}
|
||||
|
||||
// BitstreamFor returns the bitstream to reproduce the provided list of coloring operations
|
||||
// from this sequence.
|
||||
func (seq ControlWordSequence) BitstreamFor(ops []TileColorOp) ([]byte, error) {
|
||||
var writer BitstreamWriter
|
||||
writeOp := func(op TileColorOp) {
|
||||
nested := seq.opPaths[op]
|
||||
nested.writeTo(&writer)
|
||||
}
|
||||
pendingSkips := uint32(0)
|
||||
writePendingSkips := func() {
|
||||
for pendingSkips != 0 {
|
||||
toSkip := pendingSkips
|
||||
if toSkip >= 0x1F {
|
||||
toSkip = 0x1E
|
||||
}
|
||||
writeOp(TileColorOp{Type: CtrlSkip})
|
||||
writer.Write(5, toSkip-1)
|
||||
pendingSkips -= toSkip
|
||||
}
|
||||
}
|
||||
writeLineSkip := func() {
|
||||
if pendingSkips > 0 {
|
||||
writeOp(TileColorOp{Type: CtrlSkip})
|
||||
writer.Write(5, 0x1F)
|
||||
pendingSkips = 0
|
||||
}
|
||||
}
|
||||
hasWidthConfig := seq.HTiles > 0
|
||||
for opIndex, op := range ops {
|
||||
atRowStart := hasWidthConfig && ((uint32(opIndex) % seq.HTiles) == 0)
|
||||
if (pendingSkips > 0) && atRowStart {
|
||||
writeLineSkip()
|
||||
}
|
||||
if op.Type == CtrlSkip {
|
||||
pendingSkips++
|
||||
if !hasWidthConfig {
|
||||
writePendingSkips()
|
||||
}
|
||||
} else {
|
||||
writePendingSkips()
|
||||
writeOp(op)
|
||||
}
|
||||
}
|
||||
if pendingSkips > 0 {
|
||||
writeLineSkip()
|
||||
}
|
||||
return writer.Buffer(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user