Fix VPS freeze
This commit is contained in:
+70
-24
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@@ -24,9 +25,11 @@ type Archive struct {
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
Name string
|
||||
Type uint16
|
||||
Data []byte
|
||||
Name string
|
||||
Type uint16
|
||||
Data []byte
|
||||
SourcePath string
|
||||
Size int64
|
||||
}
|
||||
|
||||
type header struct {
|
||||
@@ -139,7 +142,7 @@ func New(fileType string, resources []Resource) Archive {
|
||||
}
|
||||
|
||||
func ArchiveSize(resources []Resource) int64 {
|
||||
return int64(headerSize + len(resources)*24 + len(resources)*8 + totalResourceBytes(resources))
|
||||
return int64(headerSize+len(resources)*24+len(resources)*8) + totalResourceBytes(resources)
|
||||
}
|
||||
|
||||
func Write(w io.Writer, archive Archive) error {
|
||||
@@ -154,28 +157,30 @@ func Write(w io.Writer, archive Archive) error {
|
||||
entries := make([]resourceEntry, 0, len(archive.Resources))
|
||||
|
||||
dataOffset := uint32(headerSize + len(archive.Resources)*24 + len(archive.Resources)*8)
|
||||
data := bytes.Buffer{}
|
||||
|
||||
for index, resource := range archive.Resources {
|
||||
if len(resource.Name) > 16 {
|
||||
return fmt.Errorf("resource %q exceeds 16-byte resref limit", resource.Name)
|
||||
}
|
||||
size, err := payloadSize(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size > int64(^uint32(0)) {
|
||||
return fmt.Errorf("resource %q exceeds 4 GiB resource size limit", resource.Name)
|
||||
}
|
||||
|
||||
entry := resourceEntry{
|
||||
Offset: dataOffset + uint32(data.Len()),
|
||||
Size: uint32(len(resource.Data)),
|
||||
Offset: dataOffset,
|
||||
Size: uint32(size),
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
dataOffset += uint32(size)
|
||||
|
||||
var key keyEntry
|
||||
copy(key.ResRef[:], []byte(resource.Name))
|
||||
key.ResourceID = uint32(index)
|
||||
key.ResourceType = resource.Type
|
||||
keys = append(keys, key)
|
||||
|
||||
if _, err := data.Write(resource.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hdr := header{
|
||||
@@ -192,22 +197,21 @@ func Write(w io.Writer, archive Archive) error {
|
||||
copy(hdr.FileType[:], []byte(padFour(archive.FileType)))
|
||||
copy(hdr.Version[:], []byte(padFour(archive.Version)))
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := binary.Write(&buf, binary.LittleEndian, hdr); err != nil {
|
||||
if err := binary.Write(w, binary.LittleEndian, hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(&buf, binary.LittleEndian, keys); err != nil {
|
||||
if err := binary.Write(w, binary.LittleEndian, keys); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(&buf, binary.LittleEndian, entries); err != nil {
|
||||
if err := binary.Write(w, binary.LittleEndian, entries); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.Write(data.Bytes()); err != nil {
|
||||
return err
|
||||
for _, resource := range archive.Resources {
|
||||
if err := writeResourceData(w, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := w.Write(buf.Bytes())
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func Read(r io.Reader) (Archive, error) {
|
||||
@@ -260,6 +264,7 @@ func Read(r io.Reader) (Archive, error) {
|
||||
Name: resref,
|
||||
Type: key.ResourceType,
|
||||
Data: payload,
|
||||
Size: int64(entry.Size),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -288,10 +293,51 @@ func padFour(value string) string {
|
||||
return value + strings.Repeat(" ", 4-len(value))
|
||||
}
|
||||
|
||||
func totalResourceBytes(resources []Resource) int {
|
||||
total := 0
|
||||
func totalResourceBytes(resources []Resource) int64 {
|
||||
var total int64
|
||||
for _, resource := range resources {
|
||||
total += len(resource.Data)
|
||||
switch {
|
||||
case resource.Size > 0:
|
||||
total += resource.Size
|
||||
default:
|
||||
total += int64(len(resource.Data))
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func payloadSize(resource Resource) (int64, error) {
|
||||
if resource.Size > 0 {
|
||||
return resource.Size, nil
|
||||
}
|
||||
if resource.SourcePath != "" && len(resource.Data) == 0 {
|
||||
info, err := os.Stat(resource.SourcePath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("stat resource %q: %w", resource.SourcePath, err)
|
||||
}
|
||||
return info.Size(), nil
|
||||
}
|
||||
return int64(len(resource.Data)), nil
|
||||
}
|
||||
|
||||
func writeResourceData(w io.Writer, resource Resource) error {
|
||||
if len(resource.Data) > 0 || resource.SourcePath == "" {
|
||||
_, err := w.Write(resource.Data)
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Open(resource.SourcePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open resource %q: %w", resource.SourcePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
written, err := io.Copy(w, file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy resource %q: %w", resource.SourcePath, err)
|
||||
}
|
||||
if resource.Size > 0 && written != resource.Size {
|
||||
return fmt.Errorf("copy resource %q: expected %d bytes, wrote %d", resource.SourcePath, resource.Size, written)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user