Adding modding folder with a hacked build.

This commit is contained in:
ml
2026-09-26 08:53:08 +02:00
parent cdb960dc71
commit c91080aba7
755 changed files with 50424 additions and 0 deletions
@@ -0,0 +1,91 @@
package graphics
import "github.com/inkyblackness/hacked/ui/opengl"
// BitmapTexture wraps an OpenGL handle for a downloaded image.
type BitmapTexture struct {
gl opengl.OpenGL
handle uint32
width, height float32
u, v float32
pixelData []byte
}
func powerOfTwo(value int) int {
result := 2
for (result < value) && (result < 0x1000) {
result *= 2
}
return result
}
// NewBitmapTexture downloads the provided raw data to OpenGL and returns a BitmapTexture instance.
func NewBitmapTexture(gl opengl.OpenGL, width, height int, pixelData []byte) *BitmapTexture {
textureWidth := powerOfTwo(width)
textureHeight := powerOfTwo(height)
tex := &BitmapTexture{
gl: gl,
handle: gl.GenTextures(1)[0],
width: float32(width),
height: float32(height),
pixelData: pixelData,
}
tex.u = tex.width / float32(textureWidth)
tex.v = tex.height / float32(textureHeight)
paddedData := make([]byte, textureWidth*textureHeight)
for y := 0; y < height; y++ {
inStart := y * width
outOffset := y * textureWidth
for x := 0; x < width; x++ {
value := pixelData[inStart+x]
paddedData[outOffset] = value
outOffset++
}
}
gl.BindTexture(opengl.TEXTURE_2D, tex.handle)
gl.TexImage2D(opengl.TEXTURE_2D, 0, opengl.RED, int32(textureWidth), int32(textureHeight),
0, opengl.RED, opengl.UNSIGNED_BYTE, paddedData)
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MAG_FILTER, opengl.NEAREST)
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MIN_FILTER, opengl.NEAREST)
gl.GenerateMipmap(opengl.TEXTURE_2D)
gl.BindTexture(opengl.TEXTURE_2D, 0)
return tex
}
// Dispose releases the OpenGL texture.
func (tex *BitmapTexture) Dispose() {
if tex.handle != 0 {
tex.gl.DeleteTextures([]uint32{tex.handle})
tex.handle = 0
}
}
// Handle returns the texture handle.
func (tex *BitmapTexture) Handle() uint32 {
return tex.handle
}
// PixelData returns the original paletted pixel data.
func (tex *BitmapTexture) PixelData() []byte {
return tex.pixelData
}
// Size returns the dimensions of the bitmap, in pixels.
func (tex *BitmapTexture) Size() (width, height float32) {
return tex.width, tex.height
}
// UV returns the maximum U and V values for the bitmap. The bitmap will be
// stored in a power-of-two texture, which may be larger than the bitmap.
func (tex *BitmapTexture) UV() (u, v float32) {
return tex.u, tex.v
}
@@ -0,0 +1,76 @@
package graphics
import (
"github.com/inkyblackness/hacked/ss1/content/bitmap"
"github.com/inkyblackness/hacked/ui/opengl"
)
// FrameCacheKey is used to identify a cached frame.
type FrameCacheKey uint16
// FrameCache loads bitmaps with their palettes and provides OpenGL textures.
type FrameCache struct {
gl opengl.OpenGL
textures map[FrameCacheKey]*BitmapTexture
palettes map[FrameCacheKey]*PaletteTexture
keyCounter uint16
}
// NewFrameCache returns a new instance.
func NewFrameCache(gl opengl.OpenGL) *FrameCache {
cache := &FrameCache{
gl: gl,
textures: make(map[FrameCacheKey]*BitmapTexture),
palettes: make(map[FrameCacheKey]*PaletteTexture),
}
return cache
}
// AllocateKey returns a new key to be used with uploading a new frame.
func (cache *FrameCache) AllocateKey() FrameCacheKey {
cache.keyCounter++
return FrameCacheKey(cache.keyCounter)
}
// SetTexture registers a texture based on given bitmap under given key.
// The bitmap should contain a palette, otherwise it will most likely not be displayed.
func (cache *FrameCache) SetTexture(key FrameCacheKey, width, height uint16, pixels []byte, palette *bitmap.Palette) {
cache.DropTextureForKey(key)
tex := NewBitmapTexture(cache.gl, int(width), int(height), pixels)
cache.textures[key] = tex
if palette == nil {
return
}
pal := NewPaletteTexture(cache.gl, palette)
cache.palettes[key] = pal
}
// DropTextureForKey removes the currently cached frame.
func (cache *FrameCache) DropTextureForKey(key FrameCacheKey) {
if tex, existing := cache.textures[key]; existing {
tex.Dispose()
delete(cache.textures, key)
}
if pal, existing := cache.palettes[key]; existing {
pal.Dispose()
delete(cache.palettes, key)
}
}
// HandlesForKey returns the OpenGL handles for both palette and texture for given key.
func (cache FrameCache) HandlesForKey(key FrameCacheKey) (palette uint32, texture uint32) {
if tex, existing := cache.textures[key]; existing {
texture = tex.Handle()
}
if pal, existing := cache.palettes[key]; existing {
palette = pal.Handle()
}
return
}
// Texture returns the buffered texture for given key.
func (cache *FrameCache) Texture(key FrameCacheKey) *BitmapTexture {
return cache.textures[key]
}
@@ -0,0 +1,71 @@
package graphics
import (
"encoding/binary"
"github.com/inkyblackness/hacked/ss1/content/bitmap"
"github.com/inkyblackness/hacked/ss1/resource"
"github.com/inkyblackness/hacked/ss1/world/ids"
"github.com/inkyblackness/hacked/ui/opengl"
)
// PaletteCache loads palettes and provides OpenGL textures.
type PaletteCache struct {
gl opengl.OpenGL
localizer resource.Localizer
palettes map[resource.Key]*PaletteTexture
}
// NewPaletteCache returns a new instance.
func NewPaletteCache(gl opengl.OpenGL, localizer resource.Localizer) *PaletteCache {
cache := &PaletteCache{
gl: gl,
localizer: localizer,
palettes: make(map[resource.Key]*PaletteTexture),
}
return cache
}
// InvalidateResources lets the cache remove any palette from resources that are specified in the given slice.
func (cache *PaletteCache) InvalidateResources(ids []resource.ID) {
for _, id := range ids {
for key, texture := range cache.palettes {
if key.ID == id {
texture.Dispose()
delete(cache.palettes, key)
}
}
}
}
// Palette returns the palette with given index - if available.
func (cache *PaletteCache) Palette(index int) (*PaletteTexture, error) {
key := resource.KeyOf(ids.GamePalettesStart.Plus(index), resource.LangAny, 0)
pal, existing := cache.palettes[key]
if existing {
return pal, nil
}
selector := cache.localizer.LocalizedResources(key.Lang)
view, err := selector.Select(key.ID)
if err != nil {
return nil, err
}
if view.ContentType() != resource.Palette {
return nil, resource.ErrWrongType(key, resource.Palette)
}
reader, err := view.Block(key.Index)
if err != nil {
return nil, err
}
var palette bitmap.Palette
err = binary.Read(reader, binary.LittleEndian, &palette)
if err != nil {
return nil, err
}
pal = NewPaletteTexture(cache.gl, &palette)
cache.palettes[key] = pal
return pal, nil
}
@@ -0,0 +1,70 @@
package graphics
import (
"github.com/inkyblackness/hacked/ss1/content/bitmap"
"github.com/inkyblackness/hacked/ui/opengl"
)
// PaletteTexture contains a palette stored as OpenGL texture.
type PaletteTexture struct {
gl opengl.OpenGL
handle uint32
palette bitmap.Palette
}
// NewPaletteTexture creates a new PaletteTexture instance.
func NewPaletteTexture(gl opengl.OpenGL, palette *bitmap.Palette) *PaletteTexture {
tex := &PaletteTexture{
gl: gl,
handle: gl.GenTextures(1)[0],
}
tex.Update(palette)
return tex
}
// Dispose releases the OpenGL texture.
func (tex *PaletteTexture) Dispose() {
if tex.handle != 0 {
tex.gl.DeleteTextures([]uint32{tex.handle})
tex.handle = 0
}
}
// Palette returns the original palette.
func (tex *PaletteTexture) Palette() bitmap.Palette {
return tex.palette
}
// Handle returns the texture handle.
func (tex *PaletteTexture) Handle() uint32 {
return tex.handle
}
// Update reloads the palette.
func (tex *PaletteTexture) Update(palette *bitmap.Palette) {
gl := tex.gl
const bytesPerRGBA = 4
const colors = 256
var data [colors * bytesPerRGBA]byte
for i := 0; i < colors; i++ {
entry := palette[i]
data[i*bytesPerRGBA+0] = entry.Red
data[i*bytesPerRGBA+1] = entry.Green
data[i*bytesPerRGBA+2] = entry.Blue
data[i*bytesPerRGBA+3] = 0xFF
}
data[3] = 0x00
gl.BindTexture(opengl.TEXTURE_2D, tex.handle)
gl.TexImage2D(opengl.TEXTURE_2D, 0, opengl.RGBA, int32(colors), 1, 0, opengl.RGBA, opengl.UNSIGNED_BYTE, data[:])
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MAG_FILTER, opengl.NEAREST)
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MIN_FILTER, opengl.NEAREST)
gl.GenerateMipmap(opengl.TEXTURE_2D)
gl.BindTexture(opengl.TEXTURE_2D, 0)
tex.palette = *palette
}
@@ -0,0 +1,94 @@
package graphics
import (
"github.com/inkyblackness/hacked/ss1"
"github.com/inkyblackness/hacked/ss1/content/bitmap"
"github.com/inkyblackness/hacked/ss1/resource"
"github.com/inkyblackness/hacked/ui/opengl"
)
const (
errReferenceNotFound ss1.StringError = "reference not existing"
errReferenceWrongDimensions ss1.StringError = "reference has wrong dimensions"
)
// TextureCache loads bitmaps and provides OpenGL textures.
type TextureCache struct {
gl opengl.OpenGL
localizer resource.Localizer
textures map[resource.Key]*BitmapTexture
}
// NewTextureCache returns a new instance.
func NewTextureCache(gl opengl.OpenGL, localizer resource.Localizer) *TextureCache {
cache := &TextureCache{
gl: gl,
localizer: localizer,
textures: make(map[resource.Key]*BitmapTexture),
}
return cache
}
// InvalidateResources lets the cache remove any textures from resources that are specified in the given slice.
func (cache *TextureCache) InvalidateResources(ids []resource.ID) {
for _, id := range ids {
for key, texture := range cache.textures {
if key.ID == id {
texture.Dispose()
delete(cache.textures, key)
}
}
}
}
// Texture returns the texture with given key - if available.
func (cache *TextureCache) Texture(key resource.Key) (*BitmapTexture, error) {
return cache.TextureReferenced(key, nil)
}
// TextureReferenced returns the texture with given key - if available.
// Should the underlying bitmap have to be loaded, then the given reference is taken as a basis for compressed bitmaps.
// If reference is nil, then no reference is used.
func (cache *TextureCache) TextureReferenced(key resource.Key, reference *resource.Key) (*BitmapTexture, error) {
tex, existing := cache.textures[key]
if existing {
return tex, nil
}
selector := cache.localizer.LocalizedResources(key.Lang)
view, err := selector.Select(key.ID)
if err != nil {
return nil, err
}
if view.ContentType() != resource.Bitmap {
return nil, resource.ErrWrongType(key, resource.Bitmap)
}
reader, err := view.Block(key.Index)
if err != nil {
return nil, err
}
bmp, err := bitmap.DecodeReferenced(reader, func(width, height int16) ([]byte, error) {
buf := make([]byte, int(width)*int(height))
if reference == nil {
return buf, nil
}
refTex, refExisting := cache.textures[*reference]
if !refExisting {
return nil, errReferenceNotFound
}
refWidth, refHeight := refTex.Size()
if (int16(refWidth) != width) || (int16(refHeight) != height) {
return nil, errReferenceWrongDimensions
}
copy(buf, refTex.PixelData())
return buf, nil
})
if err != nil {
return nil, err
}
tex = NewBitmapTexture(cache.gl, int(bmp.Header.Width), int(bmp.Header.Height), bmp.Pixels)
cache.textures[key] = tex
return tex, nil
}