Adding modding folder with a hacked build.
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package lgres
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1"
|
||||
"github.com/inkyblackness/hacked/ss1/resource"
|
||||
"github.com/inkyblackness/hacked/ss1/resource/lgres/internal/compression"
|
||||
"github.com/inkyblackness/hacked/ss1/resource/lgres/internal/format"
|
||||
"github.com/inkyblackness/hacked/ss1/serial"
|
||||
)
|
||||
|
||||
// Writer provides methods to write a new resource file from scratch.
|
||||
// Resources have to be created sequentially. The writer does not support
|
||||
// concurrent creation and modification of resources.
|
||||
type Writer struct {
|
||||
encoder *serial.PositioningEncoder
|
||||
|
||||
firstResourceOffset uint32
|
||||
currentResourceStartOffset uint32
|
||||
currentResource resourceWriter
|
||||
|
||||
directory []*resourceDirectoryEntry
|
||||
}
|
||||
|
||||
// ErrTargetNil is returned if the target cannot be used.
|
||||
const ErrTargetNil ss1.StringError = "target is nil"
|
||||
|
||||
// NewWriter returns a new Writer instance prepared to add resources.
|
||||
// To finalize the created file, call Finish().
|
||||
//
|
||||
// This function will write initial information to the target and will return
|
||||
// an error if the writer did. In such a case, the returned writer instance
|
||||
// will produce invalid results and the state of the target is undefined.
|
||||
func NewWriter(target io.WriteSeeker) (*Writer, error) {
|
||||
if target == nil {
|
||||
return nil, ErrTargetNil
|
||||
}
|
||||
|
||||
encoder := serial.NewPositioningEncoder(target)
|
||||
writer := &Writer{encoder: encoder}
|
||||
writer.writeHeader()
|
||||
writer.firstResourceOffset = writer.encoder.CurPos()
|
||||
|
||||
return writer, writer.encoder.FirstError()
|
||||
}
|
||||
|
||||
// ErrWriterFinished is returned if the writer was already finished.
|
||||
const ErrWriterFinished ss1.StringError = "writer is finished"
|
||||
|
||||
// CreateResource adds a new single-block resource to the current resource file.
|
||||
// This resource is closed by creating another resource, or by finishing the writer.
|
||||
func (writer *Writer) CreateResource(id resource.ID, contentType resource.ContentType,
|
||||
compressed bool) (*BlockWriter, error) {
|
||||
if writer.encoder == nil {
|
||||
return nil, ErrWriterFinished
|
||||
}
|
||||
|
||||
writer.finishLastResource()
|
||||
if writer.encoder.FirstError() != nil {
|
||||
return nil, writer.encoder.FirstError()
|
||||
}
|
||||
|
||||
var targetWriter io.Writer = serial.NewEncoder(writer.encoder)
|
||||
targetFinisher := func() {}
|
||||
resourceType := byte(0x00)
|
||||
if compressed {
|
||||
compressor := compression.NewCompressor(targetWriter)
|
||||
resourceType |= format.ResourceTypeFlagCompressed
|
||||
targetWriter = compressor
|
||||
targetFinisher = func() { compressor.Close() } // nolint: errcheck
|
||||
}
|
||||
blockWriter := &BlockWriter{target: targetWriter, finisher: targetFinisher}
|
||||
writer.addNewResource(id, contentType, resourceType, blockWriter)
|
||||
|
||||
return blockWriter, nil
|
||||
}
|
||||
|
||||
// CreateCompoundResource adds a new compound resource to the current resource file.
|
||||
// This resource is closed by creating another resource, or by finishing the writer.
|
||||
func (writer *Writer) CreateCompoundResource(id resource.ID, contentType resource.ContentType,
|
||||
compressed bool) (*CompoundResourceWriter, error) {
|
||||
if writer.encoder == nil {
|
||||
return nil, ErrWriterFinished
|
||||
}
|
||||
|
||||
writer.finishLastResource()
|
||||
if writer.encoder.FirstError() != nil {
|
||||
return nil, writer.encoder.FirstError()
|
||||
}
|
||||
|
||||
resourceType := format.ResourceTypeFlagCompound
|
||||
if compressed {
|
||||
resourceType |= format.ResourceTypeFlagCompressed
|
||||
}
|
||||
resourceWriter := &CompoundResourceWriter{
|
||||
target: serial.NewPositioningEncoder(writer.encoder),
|
||||
compressed: compressed,
|
||||
dataPaddingSize: writer.dataPaddingSizeForCompoundResource(id)}
|
||||
writer.addNewResource(id, contentType, resourceType, resourceWriter)
|
||||
|
||||
return resourceWriter, nil
|
||||
}
|
||||
|
||||
// Finish finalizes the resource file. After calling this function, the
|
||||
// writer becomes unusable.
|
||||
func (writer *Writer) Finish() (err error) {
|
||||
if writer.encoder == nil {
|
||||
return ErrWriterFinished
|
||||
}
|
||||
|
||||
writer.finishLastResource()
|
||||
|
||||
directoryOffset := writer.encoder.CurPos()
|
||||
writer.encoder.SetCurPos(format.ResourceDirectoryFileOffsetPos)
|
||||
writer.encoder.Code(directoryOffset)
|
||||
writer.encoder.SetCurPos(directoryOffset)
|
||||
writer.encoder.Code(uint16(len(writer.directory)))
|
||||
writer.encoder.Code(writer.firstResourceOffset)
|
||||
for _, entry := range writer.directory {
|
||||
writer.encoder.Code(entry)
|
||||
}
|
||||
|
||||
err = writer.encoder.FirstError()
|
||||
writer.encoder = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (writer *Writer) writeHeader() {
|
||||
header := make([]byte, format.ResourceDirectoryFileOffsetPos)
|
||||
for index, r := range format.HeaderString {
|
||||
header[index] = byte(r)
|
||||
}
|
||||
header[len(format.HeaderString)] = format.CommentTerminator
|
||||
writer.encoder.Code(header)
|
||||
writer.encoder.Code(uint32(math.MaxUint32))
|
||||
}
|
||||
|
||||
func (writer *Writer) addNewResource(id resource.ID, contentType resource.ContentType, resourceType byte, newResource resourceWriter) {
|
||||
entry := &resourceDirectoryEntry{ID: id.Value()}
|
||||
entry.setContentType(byte(contentType))
|
||||
entry.setResourceType(resourceType)
|
||||
writer.directory = append(writer.directory, entry)
|
||||
writer.currentResource = newResource
|
||||
writer.currentResourceStartOffset = writer.encoder.CurPos()
|
||||
}
|
||||
|
||||
func (writer *Writer) finishLastResource() {
|
||||
if writer.currentResource != nil {
|
||||
currentEntry := writer.directory[len(writer.directory)-1]
|
||||
currentEntry.setUnpackedLength(writer.currentResource.finish())
|
||||
currentEntry.setPackedLength(writer.encoder.CurPos() - writer.currentResourceStartOffset)
|
||||
|
||||
writer.currentResourceStartOffset = 0
|
||||
writer.currentResource = nil
|
||||
}
|
||||
writer.alignToBoundary()
|
||||
}
|
||||
|
||||
func (writer *Writer) alignToBoundary() {
|
||||
extraBytes := writer.encoder.CurPos() % format.BoundarySize
|
||||
if extraBytes > 0 {
|
||||
padding := make([]byte, format.BoundarySize-extraBytes)
|
||||
writer.encoder.Code(padding)
|
||||
}
|
||||
}
|
||||
|
||||
func (writer *Writer) dataPaddingSizeForCompoundResource(id resource.ID) (padding int) {
|
||||
// Some directories have a 2byte padding before the actual data
|
||||
idValue := id.Value()
|
||||
if (idValue >= 0x08FC) && (idValue <= 0x094B) { // all resources in obj3d.res
|
||||
padding = 2
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user