lazarus/formats/mpq/mpq.go

171 lines
3.6 KiB
Go
Raw Normal View History

2018-12-15 21:30:18 +00:00
package mpq
2019-01-04 03:38:41 +00:00
// #cgo windows CFLAGS: -D_MPQ_WINDOWS
// #cgo windows LDFLAGS: -Lstormlib -lstorm -lwininet -lz -lbz2 -lstdc++
// #cgo linux CFLAGS: -D_MPQ_LINUX
// #cgo linux LDFLAGS: -L./stormlib/ -lstorm -lz -lbz2 -lstdc++
2019-01-01 18:30:35 +00:00
// #ifdef _MPQ_WINDOWS
2019-01-04 03:38:41 +00:00
// #include "native_windows.h"
2019-01-01 18:30:35 +00:00
// #endif
// #ifdef _MPQ_LINUX
2019-01-04 03:38:41 +00:00
// #include "native_linux.h"
2019-01-01 18:30:35 +00:00
// #endif
2018-12-15 21:30:18 +00:00
import "C"
import (
"bytes"
"fmt"
"io"
"os"
2018-12-15 21:30:18 +00:00
"strings"
"unsafe"
)
type File interface {
Read(data []byte) (int, error)
Seek(offset int64, whence int) (int64, error)
Close() error
2018-12-15 21:30:18 +00:00
}
2019-01-01 18:30:35 +00:00
type MpqArchive interface {
2018-12-15 21:30:18 +00:00
OpenFile(path string) (File, error)
2018-12-16 04:48:20 +00:00
GetPaths() []string
2018-12-15 21:30:18 +00:00
Close() error
}
2019-01-01 18:30:35 +00:00
func NewFromFile(path string) (MpqArchive, error) {
2018-12-15 21:30:18 +00:00
cs := C.CString(path)
defer C.free(unsafe.Pointer(cs))
a := new(archive)
2019-01-05 17:22:20 +00:00
if result := C.SFileOpenArchive(cs, 0, 0, (*C.HANDLE)(&a.handle)); result == 0 {
2018-12-15 21:30:18 +00:00
return nil, fmt.Errorf("failed to open archive (%d)", getLastError())
}
2018-12-16 04:48:20 +00:00
if err := a.buildPathMap(); err != nil {
a.Close()
return nil, err
}
2018-12-15 21:30:18 +00:00
return a, nil
}
type file struct {
handle unsafe.Pointer
}
func (f *file) Read(data []byte) (int, error) {
var bytesRead int
2019-01-05 17:22:20 +00:00
if result := C.SFileReadFile(C.HANDLE(f.handle), unsafe.Pointer(&data[0]), C.ulong(len(data)), (*C.ulong)(unsafe.Pointer(&bytesRead)), nil); result == 0 {
lastError := getLastError()
2019-01-04 03:38:41 +00:00
if lastError == C.ERROR_HANDLE_EOF {
2018-12-18 01:26:10 +00:00
return bytesRead, io.EOF
}
return 0, fmt.Errorf("failed to read file (%d)", lastError)
2018-12-15 21:30:18 +00:00
}
return bytesRead, nil
}
func (f *file) Seek(offset int64, whence int) (int64, error) {
var method uint
switch whence {
case io.SeekStart:
2019-01-04 03:38:41 +00:00
method = C.FILE_BEGIN
case io.SeekCurrent:
2019-01-04 03:38:41 +00:00
method = C.FILE_CURRENT
case io.SeekEnd:
2019-01-04 03:38:41 +00:00
method = C.FILE_END
}
2019-01-05 17:22:20 +00:00
result := C.SFileSetFilePointer(C.HANDLE(f.handle), C.long(offset), nil, C.ulong(method))
2019-01-04 03:38:41 +00:00
if result == C.SFILE_INVALID_SIZE {
return 0, fmt.Errorf("failed to set file pointer (%d)", getLastError())
}
return int64(result), nil
2018-12-15 21:30:18 +00:00
}
func (f *file) Close() error {
2019-01-05 17:22:20 +00:00
if result := C.SFileCloseFile(C.HANDLE(f.handle)); result == 0 {
2018-12-15 21:30:18 +00:00
return fmt.Errorf("failed to close file (%d)", getLastError())
}
f.handle = nil
2018-12-16 04:48:20 +00:00
return nil
}
2018-12-15 21:30:18 +00:00
type archive struct {
handle unsafe.Pointer
2018-12-16 04:48:20 +00:00
paths map[string]string
2018-12-15 21:30:18 +00:00
}
func (a *archive) Close() error {
2019-01-05 17:22:20 +00:00
if result := C.SFileCloseArchive(C.HANDLE(a.handle)); result == 0 {
2018-12-15 21:30:18 +00:00
return fmt.Errorf("failed to close archive (%d)", getLastError())
}
a.handle = nil
a.paths = nil
return nil
}
func (a *archive) OpenFile(path string) (File, error) {
2018-12-16 04:48:20 +00:00
if pathInt, ok := a.paths[path]; ok {
path = pathInt
}
cs := C.CString(path)
2018-12-15 21:30:18 +00:00
defer C.free(unsafe.Pointer(cs))
file := new(file)
2019-01-05 17:22:20 +00:00
if result := C.SFileOpenFileEx(C.HANDLE(a.handle), cs, 0, (*C.HANDLE)(&file.handle)); result == 0 {
2018-12-15 21:30:18 +00:00
return nil, fmt.Errorf("failed to open file (%d)", getLastError())
}
return file, nil
}
2018-12-16 04:48:20 +00:00
func (a *archive) GetPaths() []string {
var extPaths []string
for extPath := range a.paths {
extPaths = append(extPaths, extPath)
2018-12-15 21:30:18 +00:00
}
2018-12-16 04:48:20 +00:00
return extPaths
}
func (a *archive) buildPathMap() error {
2018-12-15 21:30:18 +00:00
f, err := a.OpenFile("(listfile)")
if err != nil {
2018-12-16 04:48:20 +00:00
return err
2018-12-15 21:30:18 +00:00
}
defer f.Close()
2018-12-15 21:30:18 +00:00
var buff bytes.Buffer
if _, err := io.Copy(&buff, f); err != nil {
2018-12-16 04:48:20 +00:00
return err
2018-12-15 21:30:18 +00:00
}
2018-12-16 04:48:20 +00:00
a.paths = make(map[string]string)
lines := strings.Split(string(buff.Bytes()), "\r\n")
for _, line := range lines {
pathInt := strings.TrimSpace(line)
if len(pathInt) > 0 {
2018-12-28 15:44:29 +00:00
pathExt := sanitizePath(pathInt)
2018-12-16 04:48:20 +00:00
a.paths[pathExt] = pathInt
2018-12-15 21:30:18 +00:00
}
}
2018-12-16 04:48:20 +00:00
return nil
}
2018-12-28 15:44:29 +00:00
func sanitizePath(path string) string {
2018-12-16 04:48:20 +00:00
return strings.ToLower(strings.Replace(path, "\\", string(os.PathSeparator), -1))
2018-12-15 21:30:18 +00:00
}
func getLastError() uint {
return uint(C.GetLastError())
}