mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-07-05 01:28:57 +00:00
Adding support for animating all image resources.
This commit is contained in:
parent
dce735786e
commit
6a049d3e33
@ -772,57 +772,12 @@ class A2PackPartitions
|
||||
|
||||
def packPortrait(imgEl)
|
||||
{
|
||||
def num = portraits.size() + 1
|
||||
def name = imgEl.@name ?: "img$num"
|
||||
def animFrameNum = 0
|
||||
def animFlags
|
||||
def m = (name =~ /^(.*)\*(\d+)(\w*)$/)
|
||||
if (m) {
|
||||
name = m[0][1]
|
||||
animFrameNum = m[0][2].toInteger()
|
||||
animFlags = m[0][3].toLowerCase()
|
||||
def (name, animFrameNum, animFlags) = decodeImageName(imgEl.@name ?: "img$num")
|
||||
if (!portraits.containsKey(name)) {
|
||||
def num = portraits.size() + 1
|
||||
portraits[name] = [num:num, anim:new AnimBuf()]
|
||||
}
|
||||
//println "Packing 126 image named '$name'."
|
||||
def buf = parse126Data(imgEl)
|
||||
if (animFrameNum == 1) {
|
||||
def flagByte
|
||||
switch (animFlags) {
|
||||
case "" : flagByte = 0; break
|
||||
case "f" : flagByte = 0x20; break
|
||||
case "fb": flagByte = 0x40; break
|
||||
case "r" : flagByte = 0x80; break
|
||||
default : throw new Exception("Unrecognized animation flags '$animFlags'")
|
||||
}
|
||||
def newBuf = ByteBuffer.allocate(50000) // plenty of room
|
||||
buf.flip() // crazy stuff to append one buffer to another
|
||||
newBuf.put(buf)
|
||||
def endPos = newBuf.position()
|
||||
newBuf.position(0)
|
||||
newBuf.put((byte) (1 + flagByte))
|
||||
newBuf.position(endPos)
|
||||
portraits[name] = [num:num, buf:newBuf]
|
||||
}
|
||||
else if (animFrameNum > 1) {
|
||||
if (!portraits[name])
|
||||
throw new Exception("Can't find first frame for animation '$name'")
|
||||
num = portraits.size() // in other words, do not increment
|
||||
buf.flip() // crazy stuff to append one buffer to another
|
||||
buf.get() // skip 1st byte - unused flags
|
||||
def out = portraits[name].buf
|
||||
out.put(buf)
|
||||
|
||||
// Increment the frame count
|
||||
def endPos = out.position()
|
||||
out.position(0)
|
||||
def before = out.get()
|
||||
out.position(0)
|
||||
out.put((byte)(before+1))
|
||||
out.position(endPos)
|
||||
//println "$name: ${out.position()} bytes."
|
||||
}
|
||||
else
|
||||
portraits[name] = [num:num, buf:buf]
|
||||
//println "...uncompressed: ${buf.position()} bytes."
|
||||
portraits[name].anim.addImage(animFrameNum, animFlags, parse126Data(imgEl))
|
||||
}
|
||||
|
||||
def packTile(imgEl)
|
||||
@ -1830,6 +1785,11 @@ class A2PackPartitions
|
||||
textureImgs.each { image -> packTexture(image) }
|
||||
if (!grabEntireFromCache("portraits", portraits, xmlLastMod)) {
|
||||
portraitImgs.each { image -> packPortrait(image) }
|
||||
portraits.each { name, portrait ->
|
||||
println "Packing portrait $name."
|
||||
portrait.buf = portrait.anim.pack()
|
||||
portrait.anim = null
|
||||
}
|
||||
addEntireToCache("portraits", portraits, xmlLastMod)
|
||||
}
|
||||
|
||||
@ -2510,6 +2470,20 @@ end
|
||||
}
|
||||
}
|
||||
|
||||
def decodeImageName(rawName)
|
||||
{
|
||||
def name = rawName
|
||||
def animFrameNum = 1
|
||||
def animFlags
|
||||
def m = (name =~ /^(.*)\*(\d+)(\w*)$/)
|
||||
if (m) {
|
||||
name = m[0][1]
|
||||
animFrameNum = m[0][2].toInteger()
|
||||
animFlags = m[0][3].toLowerCase()
|
||||
}
|
||||
return [name, animFrameNum, animFlags]
|
||||
}
|
||||
|
||||
def dataGen(xmlPath)
|
||||
{
|
||||
// Open the XML data file produced by Outlaw Editor
|
||||
@ -2526,21 +2500,11 @@ end
|
||||
def portraitNum = 0
|
||||
dataIn.image.sort{it.@name.toLowerCase()}.each { image ->
|
||||
def category = image.@category?.toLowerCase()
|
||||
def name = image.@name
|
||||
if (category == "portrait") {
|
||||
def animFrameNum = 0
|
||||
def animFlags
|
||||
def m = (name =~ /^(.*)\*(\d+)(\w*)$/)
|
||||
if (m) {
|
||||
name = m[0][1]
|
||||
animFrameNum = m[0][2].toInteger()
|
||||
animFlags = m[0][3].toLowerCase()
|
||||
}
|
||||
if (animFrameNum <= 1) {
|
||||
++portraitNum
|
||||
out.println "const PO${humanNameToSymbol(name, false)} = $portraitNum"
|
||||
portraits[name] = [] // placeholder during dataGen phase
|
||||
}
|
||||
def (name, animFrameNum, animFlags) = decodeImageName(image.@name)
|
||||
if (category == "portrait" && animFrameNum == 1) {
|
||||
++portraitNum
|
||||
out.println "const PO${humanNameToSymbol(name, false)} = $portraitNum"
|
||||
portraits[name] = [] // placeholder during dataGen phase
|
||||
}
|
||||
}
|
||||
out.println "const PO_LAST = $portraitNum"
|
||||
@ -3443,4 +3407,130 @@ end
|
||||
}
|
||||
}
|
||||
|
||||
class AnimBuf
|
||||
{
|
||||
def animFlags
|
||||
def buffers = []
|
||||
def changeOffsets = [] as Set
|
||||
def patches = []
|
||||
|
||||
def addImage(animFrameNum, animFlags, imgBuf)
|
||||
{
|
||||
if (animFrameNum == 1)
|
||||
this.animFlags = animFlags
|
||||
buffers << imgBuf
|
||||
assert animFrameNum == buffers.size() : "Missing animation frame"
|
||||
}
|
||||
|
||||
def pack()
|
||||
{
|
||||
def buf = ByteBuffer.allocate(50000) // plenty of room
|
||||
|
||||
// If no animation, add a stub to the start of the (only) image and return it
|
||||
assert buffers.size() >= 1
|
||||
if (buffers.size() == 1) {
|
||||
buf.put((byte)0)
|
||||
buf.put((byte)0)
|
||||
buffers[0].flip() // crazy stuff to append one buffer to another
|
||||
buf.put(buffers[0])
|
||||
return buf
|
||||
}
|
||||
|
||||
// Locate the change offsets and form a set of patches
|
||||
findChangeOffsets()
|
||||
changeOffsets.sort().each { pos -> addPatch(pos) }
|
||||
println "Change offsets: ${changeOffsets.sort()}"
|
||||
println "Patches: $patches"
|
||||
|
||||
// At start of buffer, put offset to animation header, then the first frame
|
||||
def offset = buffers[0].position() + 2 // 2 for header
|
||||
buf.put((byte)(offset & 0xFF))
|
||||
buf.put((byte)((offset >> 8) & 0xFF))
|
||||
buffers[0].flip()
|
||||
buf.put(buffers[0])
|
||||
|
||||
// Now append the full animation header
|
||||
def flagByte
|
||||
switch (animFlags) {
|
||||
case "" : flagByte = 0; break
|
||||
case "f" : flagByte = 1; break
|
||||
case "fb": flagByte = 2; break
|
||||
case "r" : flagByte = 3; break
|
||||
default : throw new Exception("Unrecognized animation flags '$animFlags'")
|
||||
}
|
||||
buf.put((byte) flagByte)
|
||||
buf.put((byte)0) // used to store current anim dir
|
||||
buf.put((byte)(buffers.size() - 1)) // index of last frame
|
||||
buf.put((byte)0) // used to store current anim frame
|
||||
|
||||
// Next comes the length of each patch (they're all the same)
|
||||
int patchLength = 0
|
||||
patches.each { patch ->
|
||||
patchLength += patch.end - patch.start + 1
|
||||
}
|
||||
buf.put((byte)(patchLength & 0xFF))
|
||||
buf.put((byte)((patchLength>>8) & 0xFF))
|
||||
|
||||
// And then the length of the patch offset table
|
||||
def tblLength = (patches.size() * 2) + 1 // 1 for end of table
|
||||
buf.put((byte)(tblLength & 0xFF))
|
||||
buf.put((byte)((tblLength>>8) & 0xFF))
|
||||
|
||||
// After the animation header, write out the patch offset table.
|
||||
def prevEnd = 0
|
||||
patches.each { patch ->
|
||||
buf.put((byte)(patch.start - prevEnd))
|
||||
buf.put((byte)(patch.end - patch.start))
|
||||
//println "Patch: ${patch.start - prevEnd} ${patch.end - patch.start}"
|
||||
prevEnd = patch.end
|
||||
}
|
||||
buf.put((byte)0xFF)
|
||||
|
||||
// Finally write patch data for each image (including the base image, so
|
||||
// one can loop around from last to first.)
|
||||
buffers.each { img ->
|
||||
patches.each { patch ->
|
||||
(patch.start ..< patch.end) { pos ->
|
||||
buf.put((byte)img.get(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All done.
|
||||
return buf
|
||||
}
|
||||
|
||||
// Determine all the buffer positions that any animation frame differs from
|
||||
// the base image.
|
||||
def findChangeOffsets()
|
||||
{
|
||||
ByteBuffer base = buffers[0]
|
||||
buffers[1..-1].each { ByteBuffer buf ->
|
||||
assert base.position() == buf.position() : "internal: buffers must be equal size"
|
||||
(0..<base.position()).each { pos ->
|
||||
if (base.get(pos) != buf.get(pos))
|
||||
changeOffsets << pos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addPatch(int pos)
|
||||
{
|
||||
// See if we can glom on to the previous patch
|
||||
def last = patches.isEmpty() ? [start:0, end:0] : patches[-1]
|
||||
if (last.end > 0 && pos < last.end + 3 && pos - last.start < 254) {
|
||||
last.end = pos+1
|
||||
return
|
||||
}
|
||||
|
||||
// Skip to the right position
|
||||
while (pos - last.end >= 254) {
|
||||
last = [start:last.end+254, end:last.end+254]
|
||||
patches << last
|
||||
}
|
||||
|
||||
// And add a new patch
|
||||
patches << [start:pos, end:pos+1]
|
||||
}
|
||||
}
|
||||
}
|
@ -2989,6 +2989,95 @@ showDiskActivity: !zone
|
||||
bne -
|
||||
.done rts
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Advance all animated resources by one frame.
|
||||
; Params: X = direction change (0=no change, 1=forward, $FF=backward).
|
||||
; Only applied to resources marked as "forward/backward" order.
|
||||
; Y = number of frames to skip.
|
||||
; Only applied to resources marked as "random" order.
|
||||
advanceAnims:
|
||||
stx resType ; store direction-change
|
||||
sty resNum ; store frames-to-skip
|
||||
lda #0
|
||||
sta .ret+1 ; clear count of animated
|
||||
ldx isAuxCmd ; grab correct starting segment (0=main mem, 1=aux)
|
||||
.loop: lda tSegType,x ; segment flags and type
|
||||
bpl .next ; skip non-active
|
||||
and #$F ; get type
|
||||
cmp #RES_TYPE_PORTRAIT
|
||||
beq .anim ; found an animated resource type
|
||||
bne .next ; not animated; skip
|
||||
.anim lda tSegAdrLo,x ; pointer to start of resource
|
||||
sta pTmp
|
||||
lda tSegAdrHi,x ; ...hi byte too
|
||||
sta pTmp+1
|
||||
|
||||
ldy #1
|
||||
lda (pTmp),y ; check anim header offset
|
||||
ora (pTmp),y
|
||||
beq .next ; if zero, resource is not aniimated
|
||||
|
||||
lda (pTmp),y ; grab offset
|
||||
clc
|
||||
adc pTmp ; add to starting addr
|
||||
sta tmp ; to obtain addr of animation header
|
||||
iny
|
||||
lda (pTmp),y ; hi byte too
|
||||
adc pTmp+1
|
||||
sta tmp+1
|
||||
|
||||
txa ; save link number we're scanning
|
||||
pha
|
||||
|
||||
.chkr ldy #0
|
||||
lda (tmp),y ; get animation type (1=Forward, 2=Forward+Backward, 3=Random)
|
||||
cmp #3 ; is it random?
|
||||
bne .chkfb
|
||||
ldx resNum ; number of frames to skip
|
||||
beq .res ; if zero, nothing to do
|
||||
- lda #1 ; direction = forward
|
||||
jsr .fwd ; advance one frame
|
||||
dec resNum ; number to advance
|
||||
bne - ; loop for specified number of skips
|
||||
beq .doptch ; and go do the patching (always taken)
|
||||
|
||||
.chkfb iny ; index of current dir
|
||||
cmp #2 ; is it a forward+backward anim?
|
||||
bne .setdir
|
||||
lda resType ; get change to dir
|
||||
beq .adv ; not changing? just advance
|
||||
.setdir sta (pTmp),y ; store new dir
|
||||
|
||||
.adv lda (pTmp),y ; get current dir
|
||||
jsr .fwbk ; advance the frame number in that direction
|
||||
.doptch jsr .patch ; apply patch for the new frame
|
||||
|
||||
.res pla ; restore link number we're scanning
|
||||
tax
|
||||
.next: lda tSegLink,x ; next in chain
|
||||
tax ; to X reg index
|
||||
bne .loop ; non-zero = not end of chain - loop again
|
||||
.ret lda #0 ; return count of number actually patched (self-modified by .patch below)
|
||||
rts
|
||||
|
||||
.fwbk ldy #3 ; index of current frame number
|
||||
clc
|
||||
adc (tmp),y ; advance in direction
|
||||
dey ; index of number of frames
|
||||
bcc + ; carry can only be set if dir=-1 and we wrapped around
|
||||
lda (tmp),y ; get number of frames
|
||||
sbc #1 ; minus one (we know carry is already set)
|
||||
+ cmp (tmp),y ; are we at the limit of number of frames?
|
||||
bne +
|
||||
lda #0 ; back to start
|
||||
+ iny ; index of current frame number
|
||||
sta (tmp),y ; and store it
|
||||
rts
|
||||
|
||||
.patch inc .ret+1 ; count number we have actually changed
|
||||
; TODO
|
||||
rts
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Segment tables
|
||||
|
||||
|
@ -86,6 +86,23 @@
|
||||
; The remainder of the file is the data for the resources, in order of their
|
||||
; table appearance.
|
||||
;
|
||||
; ------------------------------------------------------------
|
||||
; Animated resource format (frame images, portraits, textures)
|
||||
; bytes 0-1: offset to animation header (or $0000 if not animated)
|
||||
; bytes 2-n: invariant image data
|
||||
; Followed by animation header:
|
||||
; byte 0: animation type (1=Forward, 2=Forward+Backward, 3=Random)
|
||||
; byte 1: current anim dir
|
||||
; byte 2: index of last frame (= number of frames *minus 1*)
|
||||
; byte 3: current anim frame
|
||||
; bytes 4-5: length of any patch data segment (they're all the same length)
|
||||
; bytes 6-7: length of patch offset table
|
||||
; Followed by patch offset table. Each entry:
|
||||
; byte 0: # of invariant bytes to skip (can be zero) ($FF for end of table)
|
||||
; byte 1: # of patch bytes to copy (can be zero)
|
||||
; Followed by patch data segments. Each segment:
|
||||
; bytes 0-n: raw data (intelligible only by using patch offset table)
|
||||
|
||||
mainLoader = $800
|
||||
auxLoader = mainLoader+3
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user