# -*- coding: utf8 -*-
#
# Copyright (C) 2015 Evol Online
# Author: Andrei Karas (4144)
import array
import os
from PIL import Image
from code.fileutils import readInt8, readInt16, readInt32, makeDir, readFile, saveFile, skipData, readData, findFileIn
class ActClass:
pass
class ActAnimationClass:
pass
class ActSpriteClass:
pass
class ActFrameClass:
pass
class ActPivotClass:
pass
class ActEventClass:
pass
class SprClass:
pass
class SprImageClass:
pass
def readIndexedRLEImage(f, spr):
spr.nimages = spr.indexedSpritesCount
spr.images = dict()
for imageN in range(0, spr.indexedSpritesCount):
image = SprImageClass()
spr.images[imageN] = image
image.width = readInt16(f)
image.height = readInt16(f)
image.compressed = True
data = array.array('B', (0 for _ in xrange(image.width * image.height)))
image.data = data
compressSize = readInt16(f)
#uncompressedSize = image.width * image.height
#print "uncompressed size=" + str(uncompressedSize)
#print "compressed size=" + str(compressSize)
idx = 0
readCnt = 0
while readCnt < compressSize:
c = readInt8(f)
readCnt = readCnt + 1
data[idx] = c;
#print "{0:4}: {1}".format(idx, c)
idx = idx + 1
if c == 0:
cnt = readInt8(f)
readCnt = readCnt + 1
if cnt == 0:
data[idx] = cnt
#print "{0:4}: {1}".format(idx, cnt)
idx = idx + 1
else:
for j in range(1, cnt):
data[idx] = c
#print "{0:4} ({1} to {2}): {3}".format(idx, j, cnt - 1, c)
idx = idx + 1
#print "read bytes: " + str(readCnt)
def readIndexedImage(f, spr):
pass
def readRgbaImage(f, spr):
for imageN in range(0, spr.rgbaSpritesCount):
image = SprImageClass()
spr.images[imageN + spr.indexedSpritesCount] = image
image.width = readInt16(f)
image.height = readInt16(f)
data = array.array('I', (0 for _ in xrange(image.width * image.height)))
image.compressed = False
image.data = data
for idx in range(0, image.width * image.height):
data[idx] = readInt32(f)
def readPalette(f, spr):
palette = array.array('I', (0 for _ in xrange(256)))
spr.palette = palette
for col in range(0, 256):
palette[col] = readInt32(f)
def getSignedNumber(number, bitLength):
mask = (2 ** bitLength) - 1
if number & (1 << (bitLength - 1)):
return number | ~mask
else:
return number & mask
def decodeSprite(spr):
palette = spr.palette
spr.maxwidth = 0
spr.maxheight = 0
alpha = False
for imageN in range(0, spr.indexedSpritesCount):
image = spr.images[imageN]
indexed = image.data
data = array.array('I', (0 for _ in xrange(image.width * image.height)))
for idx in range(0, image.width * image.height):
col = indexed[idx]
if col == 0:
data[idx] = 0
else:
if palette[col] > 0x00ffffff:
data[idx] = palette[col]
alpha = True
else:
data[idx] = palette[col] + 0xff000000
image.data = data
if image.width > spr.maxwidth:
spr.maxwidth = image.width
if image.height > spr.maxheight:
spr.maxheight = image.height
# for debug
# png = Image.new('RGBA', (image.width, image.height))
# png.putdata(image.data)
# png.save("test{0}.png".format(imageN))
if alpha:
print "detected alpha"
for imageN in range(0, spr.rgbaSpritesCount):
image = spr.images[imageN + spr.indexedSpritesCount]
if image.width > spr.maxwidth:
spr.maxwidth = image.width
if image.height > spr.maxheight:
spr.maxheight = image.height
def saveSpriteImage(act, spr, spriteDir, spriteName):
# for imageN in range(0, spr.rgbaSpritesCount):
# image = spr.images[imageN + spr.indexedSpritesCount]
# print "{0} x {1}".format(image.width, image.height)
# png = Image.new('RGBA', (image.width, image.height))
# png.putdata(image.data)
# png.save("test{0}.png".format(imageN + spr.indexedSpritesCount))
# numTiles = spr.indexedSpritesCount + spr.rgbaSpritesCount
counted = 0
for animN in range(0, act.nanimations):
anim = act.animations[animN]
for spriteN in range(0, anim.nsprites):
sprite = anim.sprites[spriteN]
# key = []
# for frameN in range(0, sprite.nframes):
# frame = sprite.frames[frameN]
# idf = frame.frameIndex
# if frame.mirror > 0:
# idf = - idf
# key.append(idf)
counted = counted + 1
numTiles = counted
#print "max: {0}x{1}".format(spr.maxwidth, spr.maxheight)
# in row
rowTiles = int(2048 / spr.maxwidth)
colTiles = int(numTiles / rowTiles)
if colTiles * rowTiles < numTiles:
colTiles = colTiles + 1
tilesetWidth = 2048
tilesetHeight = colTiles * spr.maxheight
# print "num {0} row {1}, col {2}".format(numTiles, rowTiles, colTiles)
# print "size {0}x{1}".format(2048, colTiles * spr.maxheight)
# tileset = array.array('I', (0 for _ in xrange(tilesetWidth * tilesetHeight)))
tileset = Image.new('RGBA', (tilesetWidth, tilesetHeight))
# png.putdata(tileset)
#draw = ImageDraw.Draw(png)
x = 0
y = 0
frameToIdx = dict()
tile = 0
for animN in range(0, act.nanimations):
anim = act.animations[animN]
for spriteN in range(0, anim.nsprites):
sprite = anim.sprites[spriteN]
frameToIdx[str(animN) + "_" + str(spriteN)] = tile
for frameN in range(0, sprite.nframes):
frame = sprite.frames[frameN]
frm = frame.frameIndex
if frame.mirror > 0:
frm = - frm
if frm in frameToIdx:
continue
if frame.frameIndex not in spr.images:
print "wrong frame index: {0}".format(frame.frameIndex)
continue
image = spr.images[frame.frameIndex]
png = Image.new('RGBA', (image.width, image.height))
png.putdata(image.data)
if frame.mirror > 0:
png = png.transpose(Image.FLIP_LEFT_RIGHT)
offsetX = (spr.maxwidth - image.width) / 2
offsetY = spr.maxheight - image.height
# offsetX = (spr.maxwidth - image.width) / 2 + getSignedNumber(frame.offsetX, 32)
# offsetY = spr.maxheight - image.height + getSignedNumber(frame.offsetY, 32)
tileset.paste(png, (x + offsetX, y + offsetY))
tile = tile + 1
x = x + spr.maxwidth
if x + spr.maxwidth > 2048:
x = 0
y = y + spr.maxheight
# for imageN in range(0, spr.rgbaSpritesCount + spr.indexedSpritesCount):
# image = spr.images[imageN]
# png = Image.new('RGBA', (image.width, image.height))
# png.putdata(image.data)
# tileset.paste(png, (x, y))
# x = x + spr.maxwidth
# if x + spr.maxwidth > 2048:
# x = 0
# y = y + spr.maxheight
spr.frameToIdx = frameToIdx
spr.tilesetWidth = tilesetWidth
spr.tilesetHeight = tilesetHeight
makeDir(spriteDir)
tileset.save(spriteDir + spriteName + ".png")
def extractSpriteAnimData(act, spr, actIndex, direction):
# delay = anim.delay
delay = 100
data = " <animation direction=\"" + direction + "\">\n"
if actIndex not in act.animations:
data = data + " <frame index=\"{0}\" delay=\"{1}\" offsetX=\"{2}\" offsetY=\"{3}\"/>\n".format(
0, delay, 0, 0)
data = data + " </animation>\n"
return data
anim = act.animations[actIndex]
for spriteN in range(0, anim.nsprites):
#sprite = anim.sprites[spriteN]
#for frameN in range(0, sprite.nframes):
#frame = sprite.frames[frameN]
#frm = frame.frameIndex
#if frame.mirror > 0:
# frm = -frm
#if frm not in spr.frameToIdx:
# continue
idx = spr.frameToIdx[str(actIndex) + "_" + str(spriteN)]
offsetX = 0
offsetY = 0
# offsetX = frame.offsetX
# offsetY = frame.offsetY
# if offsetX > 4294900000:
# offsetX = - (4294967296 - offsetX)
# if offsetY > 4294000000:
# offsetY = -(4294967296 - offsetY)
data = data + " <frame index=\"{0}\" delay=\"{1}\" offsetX=\"{2}\" offsetY=\"{3}\"/>\n".format(
idx, delay, offsetX, offsetY)
data = data + " </animation>\n"
return data
def extractSpriteDataAll(act, spr, actIndex, name):
data = extractSpriteAnimData(act, spr, actIndex, "down")
data = data + extractSpriteAnimData(act, spr, actIndex + 1, "downleft")
data = data + extractSpriteAnimData(act, spr, actIndex + 2, "left")
data = data + extractSpriteAnimData(act, spr, actIndex + 3, "upleft")
data = data + extractSpriteAnimData(act, spr, actIndex + 4, "up")
data = data + extractSpriteAnimData(act, spr, actIndex + 5, "upright")
data = data + extractSpriteAnimData(act, spr, actIndex + 6, "right")
data = data + extractSpriteAnimData(act, spr, actIndex + 6, "downright")
return data
def saveSpriteXml(act, spr, spriteDir, spriteName):
templatesDir = "templates/"
dstFile = spriteDir + spriteName + ".xml"
tpl = readFile(templatesDir + "sprite.xml")
# 0, 8, 16, 24, 32, 40, 48
# 0 - walk or attack or ?
# 8 - walk
# 16 - attack
# 24 - dead
# 32 - dead2 ?
# no more
standData = extractSpriteDataAll(act, spr, 0, "stand")
walkData = extractSpriteDataAll(act, spr, 8, "walk")
attackData = extractSpriteDataAll(act, spr, 16, "attack")
deadData = extractSpriteDataAll(act, spr, 32, "dead")
data = tpl.format(
src = "graphics/sprites/sprites/" + spriteName + ".png",
width = spr.maxwidth,
height = spr.maxheight,
stand = standData,
walk = walkData,
attack = attackData,
dead = deadData
)
saveFile(dstFile, data)
def readAct(actFile):
act = ActClass()
with open(actFile, "r") as f:
act.header = readInt16(f)
if act.header != 17217:
#print "Wrong header in file {0}".format(actFile)
return None
act.minorVersion = readInt8(f)
act.majorVersion = readInt8(f)
act.nanimations = readInt16(f)
print "{0}, {1}.{2}, {3}".format(actFile, act.majorVersion, act.minorVersion, act.nanimations)
#print " animations: " + str(act.nanimations)
act.animations = dict()
skipData(f, 10)
for animN in range(0, act.nanimations):
anim = ActAnimationClass()
anim.delay = 30
act.animations[animN] = anim
anim.nsprites = readInt32(f)
#print " sprites: " + str(anim.nsprites)
anim.sprites = dict()
for spriteN in range(0, anim.nsprites):
sprite = ActSpriteClass()
anim.sprites[spriteN] = sprite
sprite.left1 = readInt32(f)
sprite.top1 = readInt32(f)
sprite.right1 = readInt32(f)
sprite.buttom1 = readInt32(f)
sprite.left2 = readInt32(f)
sprite.top2 = readInt32(f)
sprite.right2 = readInt32(f)
sprite.buttom2 = readInt32(f)
sprite.nframes = readInt32(f)
#print "sprite {0}, frames: {1}".format(spriteN, sprite.nframes)
sprite.frames = dict()
for frameN in range(0, sprite.nframes):
frame = ActFrameClass()
sprite.frames[frameN] = frame
frame.offsetX = readInt32(f)
frame.offsetY = readInt32(f)
frame.frameIndex = readInt32(f)
#print " index: " + str(frame.frameIndex)
frame.mirror = readInt32(f)
if act.majorVersion >= 2:
frame.color = readInt32(f)
frame.scaleX = readInt32(f)
if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 4):
frame.scaleY = readInt32(f)
else:
frame.scaleY = frame.scaleX
frame.angle = readInt32(f)
frame.spriteType = readInt32(f)
if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 5):
frame.width = readInt32(f)
frame.height = readInt32(f)
#print "{0} x {1}".format(frame.width, frame.height)
if act.majorVersion >= 2:
sprite.eventIndex = readInt32(f)
if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 3):
sprite.npivots = readInt32(f)
sprite.pivots = dict()
#if sprite.npivots > 0:
#print " pivotes: " + str(sprite.npivots)
for pivotN in range(0, sprite.npivots):
pivot = ActPivotClass()
sprite.pivots[pivotN] = pivot
pivot.unknown = readInt32(f)
pivot.centerX = readInt32(f)
pivot.centerY = readInt32(f)
pivot.nAttribute = readInt32(f)
if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 1):
act.nevents = readInt32(f)
act.events = dict()
for eventN in range(0, act.nevents):
event = ActEventClass()
act.events[eventN] = event;
event.name = readData(f, 40)
if act.majorVersion > 2 or (act.majorVersion == 2 and act.minorVersion >= 2):
for animN in range(0, act.nanimations):
anim = act.animations[animN]
anim.delay = readInt32(f)
return act
def readSpr(sprFile):
spr = SprClass()
with open(sprFile, "r") as f:
spr.header1 = readInt8(f)
spr.header2 = readInt8(f)
if spr.header1 != 0x53 or spr.header2 != 0x50:
return None
spr.minorVersion = readInt8(f)
spr.majorVersion = readInt8(f)
spr.indexedSpritesCount = readInt16(f)
if spr.majorVersion > 1 or (spr.majorVersion == 1 and spr.minorVersion >= 1):
spr.rgbaSpritesCount = readInt16(f)
else:
spr.rgbaSpritesCount = 0
print "{0}, {1}.{2}, {3}, {4}".format(sprFile, spr.majorVersion, spr.minorVersion, spr.indexedSpritesCount, spr.rgbaSpritesCount)
spr.frames = spr.indexedSpritesCount + spr.rgbaSpritesCount
if spr.majorVersion > 2 or (spr.majorVersion == 2 and spr.minorVersion >= 1):
readIndexedRLEImage(f, spr)
else:
readIndexedImage(f, spr)
readRgbaImage(f, spr)
if spr.majorVersion > 1 or (spr.majorVersion == 1 and spr.minorVersion >= 1):
readPalette(f, spr)
return spr
def convertSprite(spritePath, spriteName):
actFile = "{0}.act".format(spritePath + spriteName)
sprFile = "{0}.spr".format(spritePath + spriteName)
if os.path.exists(actFile) == False or os.path.exists(sprFile) == False:
return None
act = readAct(actFile)
spr = readSpr(sprFile)
decodeSprite(spr)
saveSpriteImage(act, spr, "clientdata/graphics/sprites/sprites/", spriteName)
saveSpriteXml(act, spr, "clientdata/graphics/sprites/sprites/", spriteName)
# if actFile.find("wolf") > 0:
# exit(0)
# exit(0)
def findSpritePath(spriteName):
testName = spriteName + ".act"
testName2 = spriteName.upper() + ".act"
path = findFileIn((testName, testName2),
("rodata/data/sprite/ёуЅєЕН/",
"rodata/data/sprite/npc/",
"rodata/data/sprite/homun/"))
return path
def convertSpritesNonFree(idtofile):
processed = []
for spriteid in idtofile:
spriteName = idtofile[spriteid]
if spriteName in processed:
print "skipping " + spriteName
continue
path = findSpritePath(spriteName)
if path is None:
print "not found " + spriteName
continue
print spriteName
convertSprite(path, spriteName)
processed.append(spriteName)