Fix VPS freeze
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
# Local Go build cache
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Built tool output
|
||||||
|
/tools/nwn-tool
|
||||||
|
|
||||||
|
# OS/editor noise
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
+66
-20
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -27,6 +28,8 @@ type Resource struct {
|
|||||||
Name string
|
Name string
|
||||||
Type uint16
|
Type uint16
|
||||||
Data []byte
|
Data []byte
|
||||||
|
SourcePath string
|
||||||
|
Size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type header struct {
|
type header struct {
|
||||||
@@ -139,7 +142,7 @@ func New(fileType string, resources []Resource) Archive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ArchiveSize(resources []Resource) int64 {
|
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 {
|
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))
|
entries := make([]resourceEntry, 0, len(archive.Resources))
|
||||||
|
|
||||||
dataOffset := uint32(headerSize + len(archive.Resources)*24 + len(archive.Resources)*8)
|
dataOffset := uint32(headerSize + len(archive.Resources)*24 + len(archive.Resources)*8)
|
||||||
data := bytes.Buffer{}
|
|
||||||
|
|
||||||
for index, resource := range archive.Resources {
|
for index, resource := range archive.Resources {
|
||||||
if len(resource.Name) > 16 {
|
if len(resource.Name) > 16 {
|
||||||
return fmt.Errorf("resource %q exceeds 16-byte resref limit", resource.Name)
|
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{
|
entry := resourceEntry{
|
||||||
Offset: dataOffset + uint32(data.Len()),
|
Offset: dataOffset,
|
||||||
Size: uint32(len(resource.Data)),
|
Size: uint32(size),
|
||||||
}
|
}
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
|
dataOffset += uint32(size)
|
||||||
|
|
||||||
var key keyEntry
|
var key keyEntry
|
||||||
copy(key.ResRef[:], []byte(resource.Name))
|
copy(key.ResRef[:], []byte(resource.Name))
|
||||||
key.ResourceID = uint32(index)
|
key.ResourceID = uint32(index)
|
||||||
key.ResourceType = resource.Type
|
key.ResourceType = resource.Type
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
|
|
||||||
if _, err := data.Write(resource.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr := header{
|
hdr := header{
|
||||||
@@ -192,22 +197,21 @@ func Write(w io.Writer, archive Archive) error {
|
|||||||
copy(hdr.FileType[:], []byte(padFour(archive.FileType)))
|
copy(hdr.FileType[:], []byte(padFour(archive.FileType)))
|
||||||
copy(hdr.Version[:], []byte(padFour(archive.Version)))
|
copy(hdr.Version[:], []byte(padFour(archive.Version)))
|
||||||
|
|
||||||
var buf bytes.Buffer
|
if err := binary.Write(w, binary.LittleEndian, hdr); err != nil {
|
||||||
if err := binary.Write(&buf, binary.LittleEndian, hdr); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(&buf, binary.LittleEndian, keys); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, keys); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := binary.Write(&buf, binary.LittleEndian, entries); err != nil {
|
if err := binary.Write(w, binary.LittleEndian, entries); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := buf.Write(data.Bytes()); err != nil {
|
for _, resource := range archive.Resources {
|
||||||
|
if err := writeResourceData(w, resource); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_, err := w.Write(buf.Bytes())
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Read(r io.Reader) (Archive, error) {
|
func Read(r io.Reader) (Archive, error) {
|
||||||
@@ -260,6 +264,7 @@ func Read(r io.Reader) (Archive, error) {
|
|||||||
Name: resref,
|
Name: resref,
|
||||||
Type: key.ResourceType,
|
Type: key.ResourceType,
|
||||||
Data: payload,
|
Data: payload,
|
||||||
|
Size: int64(entry.Size),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,10 +293,51 @@ func padFour(value string) string {
|
|||||||
return value + strings.Repeat(" ", 4-len(value))
|
return value + strings.Repeat(" ", 4-len(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func totalResourceBytes(resources []Resource) int {
|
func totalResourceBytes(resources []Resource) int64 {
|
||||||
total := 0
|
var total int64
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
total += len(resource.Data)
|
switch {
|
||||||
|
case resource.Size > 0:
|
||||||
|
total += resource.Size
|
||||||
|
default:
|
||||||
|
total += int64(len(resource.Data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return total
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ type hakChunk struct {
|
|||||||
Name string
|
Name string
|
||||||
Assets []assetResource
|
Assets []assetResource
|
||||||
Size int64
|
Size int64
|
||||||
Resources []erf.Resource
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Build(p *project.Project) (BuildResult, error) {
|
func Build(p *project.Project) (BuildResult, error) {
|
||||||
@@ -132,7 +131,7 @@ func BuildHAKs(p *project.Project) (BuildResult, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, fmt.Errorf("create hak archive: %w", err)
|
return BuildResult{}, fmt.Errorf("create hak archive: %w", err)
|
||||||
}
|
}
|
||||||
if err := erf.Write(hakOutput, erf.New("HAK ", chunk.Resources)); err != nil {
|
if err := erf.Write(hakOutput, erf.New("HAK ", resourceSlice(chunk.Assets))); err != nil {
|
||||||
hakOutput.Close()
|
hakOutput.Close()
|
||||||
return BuildResult{}, fmt.Errorf("write hak archive: %w", err)
|
return BuildResult{}, fmt.Errorf("write hak archive: %w", err)
|
||||||
}
|
}
|
||||||
@@ -198,7 +197,7 @@ func collectAssetResources(p *project.Project) ([]assetResource, error) {
|
|||||||
|
|
||||||
for _, rel := range p.Inventory.AssetFiles {
|
for _, rel := range p.Inventory.AssetFiles {
|
||||||
abs := filepath.Join(p.AssetsDir(), filepath.FromSlash(rel))
|
abs := filepath.Join(p.AssetsDir(), filepath.FromSlash(rel))
|
||||||
resource, err := rawResource(abs)
|
resource, err := pathResource(abs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -276,6 +275,25 @@ func resourceFromJSON(path string, moduleHakOrder []string) (erf.Resource, error
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pathResource(path string) (erf.Resource, error) {
|
||||||
|
extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".")
|
||||||
|
resourceType, ok := erf.ResourceTypeForExtension(extension)
|
||||||
|
if !ok {
|
||||||
|
return erf.Resource{}, fmt.Errorf("unsupported resource extension %q", filepath.Ext(path))
|
||||||
|
}
|
||||||
|
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return erf.Resource{}, fmt.Errorf("stat %s: %w", path, err)
|
||||||
|
}
|
||||||
|
return erf.Resource{
|
||||||
|
Name: name,
|
||||||
|
Type: resourceType,
|
||||||
|
SourcePath: path,
|
||||||
|
Size: info.Size(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func rawResource(path string) (erf.Resource, error) {
|
func rawResource(path string) (erf.Resource, error) {
|
||||||
extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".")
|
extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), ".")
|
||||||
resourceType, ok := erf.ResourceTypeForExtension(extension)
|
resourceType, ok := erf.ResourceTypeForExtension(extension)
|
||||||
@@ -321,7 +339,6 @@ func planHAKChunks(p *project.Project, assets []assetResource) ([]hakChunk, erro
|
|||||||
Name: p.Config.Module.ResRef,
|
Name: p.Config.Module.ResRef,
|
||||||
Size: erf.ArchiveSize(resourceSlice(assets)),
|
Size: erf.ArchiveSize(resourceSlice(assets)),
|
||||||
Assets: assets,
|
Assets: assets,
|
||||||
Resources: resourceSlice(assets),
|
|
||||||
}
|
}
|
||||||
return []hakChunk{chunk}, nil
|
return []hakChunk{chunk}, nil
|
||||||
}
|
}
|
||||||
@@ -377,7 +394,6 @@ func splitHAKGroup(cfg project.HAKConfig, assets []assetResource) ([]hakChunk, e
|
|||||||
Index: 1,
|
Index: 1,
|
||||||
Name: cfg.Name,
|
Name: cfg.Name,
|
||||||
Assets: assets,
|
Assets: assets,
|
||||||
Resources: resourceSlice(assets),
|
|
||||||
Size: erf.ArchiveSize(resourceSlice(assets)),
|
Size: erf.ArchiveSize(resourceSlice(assets)),
|
||||||
}
|
}
|
||||||
if err := ensureUniqueChunkResources(chunk); err != nil {
|
if err := ensureUniqueChunkResources(chunk); err != nil {
|
||||||
@@ -398,14 +414,12 @@ func splitHAKGroup(cfg project.HAKConfig, assets []assetResource) ([]hakChunk, e
|
|||||||
return nil, fmt.Errorf("asset %s alone exceeds max_bytes for hak %s", asset.Rel, cfg.Name)
|
return nil, fmt.Errorf("asset %s alone exceeds max_bytes for hak %s", asset.Rel, cfg.Name)
|
||||||
}
|
}
|
||||||
current.Assets = candidateAssets
|
current.Assets = candidateAssets
|
||||||
current.Resources = candidateResources
|
|
||||||
current.Size = candidateSize
|
current.Size = candidateSize
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if candidateSize <= maxBytes {
|
if candidateSize <= maxBytes {
|
||||||
current.Assets = candidateAssets
|
current.Assets = candidateAssets
|
||||||
current.Resources = candidateResources
|
|
||||||
current.Size = candidateSize
|
current.Size = candidateSize
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -424,7 +438,6 @@ func splitHAKGroup(cfg project.HAKConfig, assets []assetResource) ([]hakChunk, e
|
|||||||
Config: cfg,
|
Config: cfg,
|
||||||
Index: current.Index + 1,
|
Index: current.Index + 1,
|
||||||
Assets: []assetResource{asset},
|
Assets: []assetResource{asset},
|
||||||
Resources: []erf.Resource{asset.Resource},
|
|
||||||
Size: erf.ArchiveSize([]erf.Resource{asset.Resource}),
|
Size: erf.ArchiveSize([]erf.Resource{asset.Resource}),
|
||||||
}
|
}
|
||||||
if current.Size > maxBytes {
|
if current.Size > maxBytes {
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user