Ir para conteúdo

Spr Extractor


dalvorsn

Posts Recomendados

  • Autor: Dalvo
  • Nome: Sprite Extractor
  • Descrição: É um extrator de sprites dos arquivos spr padrões do tibia.
  • Como funciona: Primeiramente lê-se o arquivo em modo binário, pegam-se os dados de signatura e numero de sprites, apos isso é feita uma varredura nos bytes que os offsets dos ids apontam, iterando sobre eles e gravando-os em seus respectivos pixels, e salvando como bmp. O sistema foi feito completamente em lua, não é muito vantajoso se comparado com extratores feitas em outras linguagens, mas como o objetivo era ser objeto de estudo e não uma ferramente de utilização não vejo problema nisso e estou compartilhando com a comunidade, para os que estão a aprender lua possam analisar.

Sprite Extractor

 

O sistema é composto de dois arquivos:

# readBytes.lua

dofile("./SpriteReader.lua")

function convert(num, count)
local array = {}
local aux = 0
for i = count, 2, -1 do
local val = math.floor(num/(256^(i-1)))
array[i] = val
num = num - (val*(256^(i-1)))
end
array[1] = num
return unpack(array)
end




function saveBMP(filename, width, height, pixels)

sizeArrayColors = 4 * width * height
size_1, size_2, size_3, size_4 = convert(sizeArrayColors+54, 4)
width_1, width_2, width_3, width_4 = convert(width, 4)
height_1, height_2, height_3, height_4 = convert(height, 4)

size_array_1, size_array_2, size_array_3, size_array_4 = convert(sizeArrayColors, 4)
local byteArray =
{
--bmp header
0x42, 0x4D, -- magic numbers
size_1, size_2, size_3, size_4, --0x00, 0x12, 0x00, 0x00, -- tamanho em bytes da imagem (3072)
0x00, 0x00, 0x00, 0x00, -- sem uso
0x36, 0x00, 0x00, 0x00, -- endereço de onde começam as cores
--dib header
0x28, 0x00, 0x00, 0x00, -- tamanho do dib header em bytes - 40
width_1, width_2, width_3, width_4, -- largura
height_1, height_2, height_3, height_4, -- altura
0x01, 0x00,
0x18, 0x00, -- numero de bits por pixel (24bits/pixel)
0x00, 0x00, 0x00, 0x00,
size_array_1, size_array_2, size_array_3, size_array_4, -- numeros de bytes no array de cores
0x13, 0x0B, 0x00, 0x00, -- resolução horizontal bit/meter
0x13, 0x0B, 0x00, 0x00, -- resolução horizontal bit/meter
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
}

local file = io.open(filename..".bmp", "w+b")
for _, byte in pairs(byteArray) do
file:write(type(byte) == "number" and string.char(byte) or byte)
end

for y = height , 1, -1 do
for x = 1, width do
for _, byte in pairs(pixels[x][y]) do
file:write(string.char(byte))
end
end
end
file:close()
end

function main()
local file = io.open("./Tibia.spr", "rb")
if(file)then
reader = SpriteReader:load(file)
reader.version = reader:getUint32()
reader.numberOfSprites = reader:getUint16()
io.write("Version: ",reader.version,"\nNumber Of Sprites: ", reader.numberOfSprites, "\n")

if not(type(io.open("./Sprites")) == "userdata")then
os.execute("mkdir Sprites")
end
print "Extraindo, aguarde ..."
for id = 1, reader.numberOfSprites do
--print("Extraindo: "..id.."/"..reader.numberOfSprites)
saveBMP("./Sprites/"..tostring(id), 32, 32, reader:getPixelsData(id))
end
print("Tempos gasto na extração: "..os.clock())
reader:closeData()
else
print("Arquivo .spr não encontrado")
end
end

main()


 

# SpriteReader.lua

SpriteReader = {}

--[[
* Inicia classe com atributo do tipo userdata 'data'
* Seta atributos default e le data no modo "*a"
--]]
function SpriteReader:load(data)
attr = {version = 0, numberOfSprites = 0, posByte = 1,file = data, data = data:read("*a"), id = 1}
return setmetatable(attr, {__index = self})
end

--[[
* Pega os valores minimo e maximos para numero de bytes fornecidos
* signed é uma variavel boolean, quando true rearranja os limites para tipo signed
--]]
function SpriteReader:getRanges(byte, signed)
local min, max = 0, ((256^byte) -1)
if(signed)then
min = -max/2
max = max/2
end
return min, max
end


--[[
* le byte a byte somando seus valores 'byte' numero de vezes
* se signed é true, reajusta o valor para signed
--]]
function SpriteReader:readBytes(byte, signed)
local min, _ = self:getRanges(byte, signed)
local value = 0
for var = 1, byte do
value = value + ( self:nextByte() * (256^(var-1)) )
end
return value + min
end

--[[
* le 'posByte', que é um atributo de controle, serve apaenas para armazenar o proximo byte
* reajusta 'posByte' para posição seguinte
* retorna 'byte'
--]]
function SpriteReader:nextByte()
local byte = self.data:byte(self.posByte)
self.posByte = self.posByte + 1
return tonumber(byte)
end


function SpriteReader:seek(offset)
self.posByte = offset + 1
end
--[[
* faz a leitura byte a byte 8 vezes somando seus valores pela progressão:
* (byte) ^ 1 + (byte) ^ 2 + ... + (byte) ^ n
* valido para todos unsigneds abaixo
--]]
function SpriteReader:getUint64()
return self:readBytes(8, false)
end

function SpriteReader:getUint32()
return self:readBytes(4, false)
end

function SpriteReader:getUint16()
return self:readBytes(2, false)
end

function SpriteReader:getUint8()
return self:readBytes(1, false)
end

--[[
* segue a mesma lógica anterior, com uma unica diferença
* o valor final é reajustado para signed
--]]
function SpriteReader:getInt64()
return self:readBytes(8, true)
end

function SpriteReader:getInt32()
return self:readBytes(4, true)
end

function SpriteReader:getInt16()
return self:readBytes(2, true)
end

function SpriteReader:getInt8()
return self:readBytes(1, true)
end

--[[
* fecha o atributo file
--]]
function SpriteReader:closeData()
return self.file:close()
end

--[[
* retorna os uma tabela onde table[x][y] representam os respectivos pixels (x, y)
--]]
function SpriteReader:getPixelsData(id)
self:seek(6 + id * 4)
self:seek(self:getUint32() + 3)

local pixel, pixels = 0, {}

local offset = self.posByte + self:getUint16()
for x = 1, 32 do
pixels[x] = {}
for y = 32 , 1, -1 do
pixels[x][y] = {255, 0, 255}
end
end

while self.posByte < offset do
local tp = self:getUint16()
local colored = self:getUint16()
pixel = pixel + tp
for x = 1, colored do
local r,g,b = self:getUint8(), self:getUint8(), self:getUint8()
pixels[pixel%32+1][math.floor(pixel/32+1)] = {b, g, r}
pixel = pixel + 1
end
end
--]]
return pixels
end


Os arquivos devem ser colocados juntos em uma pasta, e a pasta deve contem o arquivo Tibia.spr, pode ser outros nomes ou diretorios, porem precisará modificar no script (qualquer noob faz).

 

Me sinto na obrigação de atribuir crédito a 3 pessoas:Jo3Bingham (pelo tutorial sobre estrutura do spr), Colex (atravès de um script dele pude intender como são organizados os pixels no spr), baxnie (por me ajudar a entender a estrutura do spr, que não obtive total exito atraves do tutorial de Jo3Bingham).

 

Para usários de Windows aconselho Scite, e para linux gosto de usar o Geany.

Editado por CyberBot
Link para o comentário
Compartilhar em outros sites

  • 7 months later...

Cara... não itendi nada iso da pra stari as imagens da tibia.spr ? pq aki ja abaixei 2 tpw de tibia.spr os dois da erro =(

Leia as regras do xtibia.com antes de postar, apos se registrar você aceito tais termos!

Caso ficar quebrando as, sera alertado!

 

Ultimo aviso!

Link para o comentário
Compartilhar em outros sites

Cara... não itendi nada iso da pra stari as imagens da tibia.spr ? pq aki ja abaixei 2 tpw de tibia.spr os dois da erro =(

 

me fale quais erros dão, e quais versões você tentou usar, versoes mais recentes tiveram uma pequena mudança na estrutura do spr, no arranjo de bytes onde grava o numero de sprites no spr, cresceu dois bytes se não me engano

 

 

Cara... não itendi nada iso da pra stari as imagens da tibia.spr ? pq aki ja abaixei 2 tpw de tibia.spr os dois da erro =(

Leia as regras do xtibia.com antes de postar, apos se registrar você aceito tais termos!

Caso ficar quebrando as, sera alertado!

 

Ultimo aviso!

Poderia me mostrar onde especificamente está escrito que não se pode postar em tópicos com mais de 30 dias de inatividade?

E mesmo que haja de fato isso escrito em algum lugar, o usuario maiconui apenas queria esclarecer uma duvida, que mal há nisso?

Unico aviso que vi foi esse:

 

Cuidado, XTibiano!

dalvorsn, o último post deste tópico tem mais de 30 dias e uma nova postagem agora poderá ser considerada como flood! Consideramos flood comentários sem sentido só para fazer o tópico subir na posição! Fique ligado e ajude a manter um fórum limpo!

 

E ele é bem claro, diz respeito a flood.

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...