Upgraded Hatenatools to the latest version

This commit is contained in:
Rix565 authored and Peder B. Sundt committed 2020-12-17 16:35:20 +01:00
1 parent e21e637923
commit 545fd41b03
3 files changed
+1892 -1672

No files matched your search

+304 -272
View File
@@ -1,273 +1,305 @@
#NTFT.py by pbsds #NTFT.py by pbsds
#AGPL3 licensed #AGPL3 licensed
# #
#PIL is required to read and write images to disk #PIL is required to read and write images to disk
# #
#Credits: #Credits:
# #
# -The guys behind TiledGGD. This sped up my work a lit. # -The guys behind TiledGGD. This sped up my work a lit.
# -Jsafive for supplying .ugo files # -Jsafive for supplying .ugo files
# #
import sys, os import sys, os, numpy as np
try: try:
import Image import Image
hasPIL = True hasPIL = True
except ImportError: except ImportError:
hasPIL = False hasPIL = False
#helpers #helpers
def AscDec(ascii, LittleEndian=False):#Converts a ascii string into a decimal def AscDec(ascii, LittleEndian=False):#Converts a ascii string into a decimal
ret = 0 ret = 0
l = map(ord, ascii) l = map(ord, ascii)
if LittleEndian: l.reverse() if LittleEndian: l.reverse()
for i in l: for i in l:
ret = (ret<<8) | i ret = (ret<<8) | i
return ret return ret
def DecAsc(dec, length=None, LittleEndian=False):#Converts a decimal into an ascii string of chosen length def DecAsc(dec, length=None, LittleEndian=False):#Converts a decimal into an ascii string of chosen length
out = [] out = []
while dec <> 0: while dec <> 0:
out.insert(0, dec&0xFF) out.insert(0, dec&0xFF)
dec >>= 8 dec >>= 8
#"".join(map(chr, out)) #"".join(map(chr, out))
if length: if length:
if len(out) > length: if len(out) > length:
#return "".join(map(chr, out[-length:])) #return "".join(map(chr, out[-length:]))
out = out[-length:] out = out[-length:]
if len(out) < length: if len(out) < length:
#return "".join(map(chr, [0]*(length-len(out)) + out)) #return "".join(map(chr, [0]*(length-len(out)) + out))
out = [0]*(length-len(out)) + out out = [0]*(length-len(out)) + out
if LittleEndian: out.reverse() if LittleEndian: out.reverse()
return "".join(map(chr, out)) return "".join(map(chr, out))
def clamp(value, min, max): def clamp(value, min, max):
if value > max: return max if value > max: return max
if value < min: return min if value < min: return min
return value return value
#Class NTFT: #Class NTFT:
# #
# The NTFT image format stores RGB values as 5 bits each: a value between 0 and 31. # The NTFT image format stores RGB values as 5 bits each: a value between 0 and 31.
# It has 1 bit of transparancy, which means its either vidssible or invisible. No gradients. # It has 1 bit of transparancy, which means its either vidssible or invisible. No gradients.
# #
# How to use: # How to use:
# #
# Converting to NTFT file: # Converting to NTFT file:
# #
# image = ReadImage(input_path) # image = ReadImage(input_path)
# NTFT().SetImage(image).WriteFile(output_path) # NTFT().SetImage(image).WriteFile(output_path)
# #
# Reading NTFT file: # Reading NTFT file:
# #
# ntft = NTFT().ReadFile(input_path, (width, height)) # ntft = NTFT().ReadFile(input_path, (width, height))
# WriteImage(ntft.Image, output_path) # WriteImage(ntft.Image, output_path)
# #
class NTFT: class NTFT:
def __init__(self): def __init__(self):
self.Loaded = False self.Loaded = False
def ReadFile(self, path, size): def ReadFile(self, path, size):
f = open(path, "rb") f = open(path, "rb")
ret = self.Read(f.read(), size) ret = self.Read(f.read(), size)
f.close() f.close()
return ret return ret
def Read(self, data, (w, h)): def Read(self, data, (w, h)):
#the actual stored data is a image with the sizes padded to the nearest power of 2. The image is then clipped out from it. #the actual stored data is a image with the sizes padded to the nearest power of 2. The image is then clipped out from it.
psize = [] psize = []
for i in (w, h): for i in (w, h):
p = 1 p = 1
while 1<<p < i: while 1<<p < i:
p += 1 p += 1
psize.append(1<<p) psize.append(1<<p)
pw, ph = psize pw, ph = psize
#check if it fits the file: #check if it fits the file:
if pw*ph*2 <> len(data): if pw*ph*2 <> len(data):
print "Invalid sizes" print "Invalid sizes"
return False return False
#JUST DO IT! #JUST DO IT!
self.Image = [[None for _ in xrange(h)] for _ in xrange(w)] #self.Image = [[None for _ in xrange(h)] for _ in xrange(w)]
for y in xrange(h): self.Image = np.zeros((w, h), dtype=">u4")
for x in xrange(w): for y in xrange(h):
pos = (x + y*pw)*2 for x in xrange(w):
byte = AscDec(data[pos:pos+2], True) pos = (x + y*pw)*2
byte = AscDec(data[pos:pos+2], True)
#ARGB1555 -> RGBA8:
a = (byte >> 15 ) * 0xFF #ARGB1555 -> RGBA8:
b = (byte >> 10 & 0x1F) * 0xFF / 0x1F a = (byte >> 15 ) * 0xFF
g = (byte >> 5 & 0x1F) * 0xFF / 0x1F b = (byte >> 10 & 0x1F) * 0xFF / 0x1F
r = (byte & 0x1F) * 0xFF / 0x1F g = (byte >> 5 & 0x1F) * 0xFF / 0x1F
r = (byte & 0x1F) * 0xFF / 0x1F
self.Image[x][y] = (r<<24) | (g<<16) | (b<<8) | a#RGBA8
#self.Image[x][y] = (r<<24) | (g<<16) | (b<<8) | a#RGBA8
self.Loaded = True self.Image[x, y] = (r<<24) | (g<<16) | (b<<8) | a#RGBA8
return self
def WriteFile(self, path): self.Loaded = True
if self.Loaded: return self
f = open(path, "wb") def WriteFile(self, path):
f.write(self.Pack()) if self.Loaded:
f.close() f = open(path, "wb")
return True f.write(self.Pack())
else: f.close()
return False return True
def Pack(self): else:
if not self.Loaded: return False
return False def Pack(self):
if not self.Loaded:
w = len(self.Image[0]) return False
h = len(self.Image)
#h = len(self.Image[0])
#the actual stored data is a image with the sizes padded to the nearest power of 2 #w = len(self.Image)
psize = [] w, h = self.Image.shape
for i in size:
p = 1 #the actual stored data is a image with the sizes padded to the nearest power of 2
while 1<<p < i: psize = []
p += 1 for i in (w, h):
padded_size.append(1<<p) p = 1
while 1<<p < i:
out = [] p += 1
for y in xrange(psize[1]): psize.append(1<<p)
for x in xrange(psize[0]):
#read out = []
c = self.Image[clamp(x, 0, w-1)][clamp(y, 0, h-1)] for y in xrange(psize[1]):
r = c >> 24 for x in xrange(psize[0]):
g = (c >> 16) & 0xFF #read
b = (c >> 8 ) & 0xFF #c = self.Image[clamp(x, 0, w-1)][clamp(y, 0, h-1)]
a = c & 0xFF c = self.Image[clamp(x, 0, w-1), clamp(y, 0, h-1)]
r = c >> 24
#convert g = (c >> 16) & 0xFF
a = 1 if a >= 0x80 else 0 b = (c >> 8 ) & 0xFF
r = r * 0x1F / 0xFF a = c & 0xFF
g = g * 0x1F / 0xFF
b = b * 0x1F / 0xFF #convert
a = 1 if a >= 0x80 else 0
#store r = r * 0x1F / 0xFF
out.append(DecAsc((a<<15) | (b<<10) | (g<<5) | r, 2, True)) g = g * 0x1F / 0xFF
b = b * 0x1F / 0xFF
return "".join(out)
def SetImage(self, Image): #store
self.Image = Image out.append(DecAsc((a<<15) | (b<<10) | (g<<5) | r, 2, True))
self.Loaded = True
return self return "".join(out)
def SetImage(self, Image):
#Function WriteImage: self.Image = Image
# self.Loaded = True
# Writes a 2D list of uint32 RGBA values as a image files. return self
# Designed to work with NTFT.Image
# #Function WriteImage:
# This function requires the PIl imaging module #
def WriteImage(image, outputPath): # Writes a 2D array of uint32 RGBA values as a image file.
if not hasPIL or not image: return False # Designed to work with NTFT.Image
#
out = [] # This function requires the PIl imaging module
for y in xrange(len(image[0])): def WriteImage(image, outputPath):
for x in xrange(len(image)): if not hasPIL:
out.append(DecAsc(image[x][y], 4)) print "Error: PIL not found!"
return False
out = Image.fromstring("RGBA", (len(image), len(image[0])), "".join(out)) #if not image: return False
filetype = outputPath[outputPath.rfind(".")+1:] out = image.tostring("F")
out.save(outputPath, filetype)
# out = []
return True # for y in xrange(len(image[0])):
# for x in xrange(len(image)):
#Function ReadImage: # out.append(DecAsc(image[x][y], 4))
#
# Returns a 2D list of uint32 RGBA values of the image file. out = Image.fromstring("RGBA", (len(image), len(image[0])), out)
# This can be passed into NTFT().SetImage()
# filetype = outputPath[outputPath.rfind(".")+1:]
# This function requires the PIl imaging module out.save(outputPath, filetype)
def ReadImage(path):
if not hasPIL: return False return True
image = Image.open(path) #Function ReadImage:
pixeldata = image.getdata() #
w, h = image.size # Returns a 2D list of uint32 RGBA values of the image file.
# This can be passed into NTFT().SetImage()
if len(pixeldata[0]) < 4: #
def Combine((r, g, b)): # This function requires the PIl imaging module
return (r << 24) | (g << 16) | (b << 8) | 0xFF def ReadImage(path):#TODO: make it support numpy
else: if not hasPIL: return False
def Combine((r, g, b, a)):
return (r << 24) | (g << 16) | (b << 8) | a image = Image.open(path)
pixeldata = image.getdata()
ret = [] w, h = image.size
for x in xrange(w):
line = [] if len(pixeldata[0]) < 4:
for y in xrange(h): def Combine((r, g, b)):
line.append(Combine(pixeldata[y*w + x])) return (r << 24) | (g << 16) | (b << 8) | 0xFF
ret.append(line) else:
def Combine((r, g, b, a)):
return ret return (r << 24) | (g << 16) | (b << 8) | a
#ret = []
ret = np.zeros((w, h), dtype=">u4")
#testing: for x in xrange(w):
# i = NTFT().ReadFile("NTFTtests/kaeru.ntft", (36, 30)) #line = []
# WriteImage(i.Image, "NTFTtests/kaeru.png") for y in xrange(h):
ret[x, y] = Combine(pixeldata[y*w + x])#maybe make a more numpy efficient way?
# i = NTFT().ReadFile("NTFTtests/News.ntft", (32, 32)) #line.append(Combine(pixeldata[y*w + x]))
# WriteImage(i.Image, "NTFTtests/News.png") #ret.append(line)
# i = NTFT().ReadFile("NTFTtests/Special Room.ntft", (32, 32)) return ret
# WriteImage(i.Image, "NTFTtests/Special Room.png")
#i = NTFT()
#i.Loaded = True #testing:
#i.Image = ReadImage("NTFTtests/geh.png") # i = NTFT().ReadFile("NTFTtests/kaeru.ntft", (36, 30))
#i.WriteFile("NTFTtests/geh.ntft") # WriteImage(i.Image, "NTFTtests/kaeru.png")
# i = NTFT().ReadFile("NTFTtests/News.ntft", (32, 32))
# WriteImage(i.Image, "NTFTtests/News.png")
if __name__ == "__main__":
print " == NTFT.py ==" # i = NTFT().ReadFile("NTFTtests/Special Room.ntft", (32, 32))
print " == by pbsds ==" # WriteImage(i.Image, "NTFTtests/Special Room.png")
print " == v0.72 =="
print #i = NTFT()
#i.Loaded = True
if not hasPIL: #i.Image = ReadImage("NTFTtests/geh.png")
print "PIL not found! Exiting..." #i.WriteFile("NTFTtests/geh.ntft")
if len(sys.argv) < 3: if __name__ == "__main__":
print "Usage:" print " == NTFT.py =="
print " NTFT.py <input> [<output> [<width> <height>]]" print " == by pbsds =="
print "" print " == v0.95 =="
print "Can convert a NTFT to PNG or the other way around." print
print "if <output> isn't spesified it will be set to <input> with an another extention"
print "" if not hasPIL:
print "The NTFT file contain only the colordata, so it's up to the user to find or" print "PIL not found! Exiting..."
print "store the resolution of the image. <width> and <height> is required" sys.exit()
print "to convert a NTFT file to a image."
print "32x32 is the normal resolution for button icons in UGO files." if len(sys.argv) < 2:
sys.exit() print "Usage:"
print " NTFT.py <input> [<output> [<width> <height>]]"
input = sys.argv[1] print ""
Encode = True#if false it'll decode print "Can convert a NTFT to PNG or the other way around."
print "if <output> isn't specified it will be set to <input> with an another extension"
if input[-4:].lower == "ntft" or len(sys.argv) >= 5: print ""
Encode = False print "The NTFT file contain only the colordata, so it's up to the user to find or"
print "store the resolution of the image. <width> and <height> is required"
if len(sys.argv) >= 3: print "to convert a NTFT file to a image."
output = sys.argv[2] print "32x32 is the normal resolution for button icons in UGO files."
sys.exit()
if len(sys.argv) >= 5:
if (not sys.argv[3].isdigit()) or (not sys.argv[4].isdigit()): input = sys.argv[1]
print "Invalid sizes"
sys.exit() if input[-4:].lower() == "ntft" or len(sys.argv) >= 5:
width = int(sys.argv[3]) print "Mode: NTFT -> image"
height = int(sys.argv[4]) Encode = False
else: else:
width, height = None, None print "Mode: image -> NTFT"
else: Encode = True#if false it'll decode
output = ".".join(input.split(".")[:-1]) + (".ntft" if Encode else ".png")
if len(sys.argv) >= 3:
print "Converting..." output = sys.argv[2]
if Encode:
i = NTFT() width, height = None, None
i.Loaded = True if len(sys.argv) >= 5:
i.Image = ReadImage(input) if (not sys.argv[3].isdigit()) or (not sys.argv[4].isdigit()):
i.WriteFile(output) print "Invalid size input!"
else: sys.exit()
WriteImage(NTFT().ReadFile(input, (width, height)).Image, output) width = int(sys.argv[3])
height = int(sys.argv[4])
if not (width and height) and not Encode:
print "Image size not provided!"
sys.exit()
else:
output = ".".join(input.split(".")[:-1]) + (".ntft" if Encode else ".png")
print "Converting..."
if Encode:
try:
image = ReadImage(input)
except IOError as err:
print err
sys.exit()
i = NTFT()
i.Loaded = True
i.Image = image
i.WriteFile(output)
else:
try:
ntft = NTFT().ReadFile(input, (width, height))
except IOError as err:
print err
sys.exit()
if not ntft:#eeror message already printed
sys.exit()
WriteImage(ntft.Image, output)
print "Done!" print "Done!"
+964 -776
View File
File diff suppressed because it is too large. Load diff
+624 -624
View File
File diff suppressed because it is too large. Load diff