Initial commit

This commit is contained in:
pbsds committed 2014-01-26 05:30:04 +01:00
commit a73140ee37
36 files changed
+4081

No files matched your search

+140
View File
@@ -0,0 +1,140 @@
#NTFT.py by pbsds
#Free to use as long as credit is given to pbsds / Peder Bergebakken Sundt
#PIL is required to write images to disk
#
#Credit:
#
# -The guys behind TiledGGD at https://code.google.com/p/tiledggd/
# This sped up the my work a lot
import sys
try:
import Image
hasPIL = True
except ImportError:
hasPIL = False
#helpers
def AscDec(ascii, LittleEndian=False):#Converts a ascii string into a decimal
ret = 0
l = map(ord, ascii)
if LittleEndian: l.reverse()
for i in l:
ret = (ret<<8) | i
return ret
def DecAsc(dec, length=None, LittleEndian=False):#Converts a decimal into an ascii string of chosen length
out = []
while dec <> 0:
out.insert(0, dec&0xFF)
dec >>= 8
#"".join(map(chr, out))
if length:
if len(out) > length:
#return "".join(map(chr, out[-length:]))
out = out[-length:]
if len(out) < length:
#return "".join(map(chr, [0]*(length-len(out)) + out))
out = [0]*(length-len(out)) + out
if LittleEndian: out.reverse()
return "".join(map(chr, out))
#Class NTFT:
#
# todo: write documentation
class NTFT:
def __init__(self):
self.Loaded = False
def ReadFile(self, path, size):
f = open(path, "rb")
ret = self.Read(f.read(), size)
f.close()
return ret
def Read(self, data, (w, h)):
if w*h*2 <> len(data):
print "Invalid size"
return False
self.Image = [[None for _ in xrange(h)] for _ in xrange(w)]
for y in xrange(h):
for x in xrange(w):
pos = (x + y*w)*2
#print pos
byte = AscDec(data[pos:pos+2], True)
byte = AscDec(data[pos:pos+2], True)
#ARGB1555 -> RGBA8:#require true
a = (byte >> 15 ) * 0xFF
b = (byte >> 10 & 0x1F) * 0xFF / 0x1F
g = (byte >> 5 & 0x1F) * 0xFF / 0x1F
r = (byte & 0x1F) * 0xFF / 0x1F
# #RGB565 -> RGBA8:
# a = 0xFF
# r = (byte >> 11 & 0x1F) * 0xFF / 0x1F
# g = (byte >> 5 & 0x3F) * 0xFF / 0x3F
# b = (byte & 0x1F) * 0xFF / 0x1F
#print x, y
self.Image[x][y] = (r<<24) | (g<<16) | (b<<8) | a#RGBA8
self.Loaded = True
return self
def WriteFile(self, path):
pass
def Pack(self):
pass
#Function WriteImage:
#
# Writes a 2D list if uint32 RGBA values as a image files.
# Designed to work with PPM().Thumbnail or PPM().GetFrame(n)
#
# This function requires the PIl imaging module
def WriteImage(image, outputPath):
if not hasPIL or not image: return False
out = []
for y in xrange(len(image[0])):
for x in xrange(len(image)):
out.append(DecAsc(image[x][y], 4))
out = Image.fromstring("RGBA", (len(image), len(image[0])), "".join(out))
filetype = outputPath[outputPath.rfind(".")+1:]
out.save(outputPath, filetype)
return True
#testing:
# i = NTFT().ReadFile("NTFTtests/kaeru.ntft", (64, 32))
# WriteImage(i.Image, "NTFTtests/kaeru.png")
# i = NTFT().ReadFile("NTFTtests/News.ntft", (32, 32))
# WriteImage(i.Image, "NTFTtests/News.png")
# i = NTFT().ReadFile("NTFTtests/Special Room.ntft", (32, 32))
# WriteImage(i.Image, "NTFTtests/Special Room.png")
if __name__ == "__main__":
print " == NTFT.py =="
print " == by pbsds =="
print " == v0.05 =="
print
if len(sys.argv) < 4:
print "Usage:"
print " NTFT.py <input> <output> <width> <height>"
print ""
print "The NTFT file contain only the colordata, so it's up to the user to find and"
print "store the resolution of the images."
print "32x32 is the normal size for button icons in UGO files."
sys.exit()
input = sys.argv[1]
output = sys.argv[2]
width = int(sys.argv[3])
height = int(sys.argv[4])
print "Converting..."
WriteImage(NTFT().ReadFile(input, (width, height)).Image, output)
print "Done!"
+776
View File
@@ -0,0 +1,776 @@
#PPM.py by pbsds for python 2.7
#Free to use. Publicated modifications must give credit to pbsds / Peder Bergebakken Sundt
#PIL is required to write frames and thumbnails to disk
#
#Credits:
#
# -Steven for most of the documentation on DSiBrew and his frame decoding example on his talkpage
# -Remark for help on the 8x8 tiling on the preview image.
# -Austin Burk and Jsafive for supplying .tmb files
import sys, wave#needs os and time aswell
try:
import Image
hasPIL = True
except ImportError:
hasPIL = False
#helpers:
def AscDec(ascii, LittleEndian=False):#Converts a ascii string into a decimal
ret = 0
l = map(ord, ascii)
if LittleEndian: l.reverse()
for i in l:
ret = (ret<<8) | i
return ret
def DecAsc(dec, length=None, LittleEndian=False):#Converts a decimal into an ascii string of chosen length
out = []
while dec <> 0:
out.insert(0, dec&0xFF)
dec >>= 8
#"".join(map(chr, out))
if length:
if len(out) > length:
#return "".join(map(chr, out[-length:]))
out = out[-length:]
if len(out) < length:
#return "".join(map(chr, [0]*(length-len(out)) + out))
out = [0]*(length-len(out)) + out
if LittleEndian: out.reverse()
return "".join(map(chr, out))
def AddPadding(i,pad = 0x10):#used mainly for zipaligning offsets
if i % pad <> 0:
return i + pad - (i % pad)
return i
#Class PPM:
#
# With this class you can read a Flipnote PPM file into memory and get its attributes.
# You do so by using either instance.ReadFile() or instance.Read() of your instance of the PPM class.
# When reading the PPM file you can specify what parts you need by passing these parameters:
# - DecodeThumbnail = True: The thumbnail will be predecoded. GetThumbnail() will still work if set to False, only takes a bit longer. Default is False
# - ReadFrames = True: The frames are decoded. Disabling this will speed up the PPM reading a lot. Default is True
# - ReadSound = True: Reads the sound data. Default is False
#
# After reading a PPM file, these values are available:
#
# - instance.FrameCount: The number of frames this flipnote consists of
# - instance.Locked: A boolean telling us wether it's locked or not
# - instance.ThumbnailFrameIndex: Which frame the thumbnail is(starting at 0)
# - instance.OriginalAuthorName: The name of the original author in unicode
# - instance.EditorAuthorName: The name of the (previous?) editing author in unicode
# - instance.Username: The username in unicode
# - instance.OriginalAuthorID: The ID of the original author
# - instance.EditorAuthorID: The ID of the last editor, the last user to save the file
# - instance.PreviousEditAuthorID: The ID of the previous editor
# - instance.OriginalFilename: The original filename
# - instance.CurrentFilename: The current filename
# - instance.Date: The date in seconds since January 1'st 2000
# - instance.GetThumbnail(): The thumbnail stored in a 2D list of uint32 RGBA values. Will decode it first if not done so already
# - instance.GetFrame(n): Returns the specified frame as a 2D list of uint32 RGBA values. Only works if ReadFrames was true when reading the PPM file
# - instance.Looped: A boolean telling us wether it's looped or not
# - instance.SFXUsage: A list telling us when the different sound effects are used:
# instance.SFXUsage[frame] = [SFX1, SFX2, SFX3] where the SFX's are booleans
# - instance.Framespeed: a value between 1 and 8 telling us the speed
# - instance.BGMFramespeed: The framespeed when the BGM was recorded. used for resampling
# - instance.Frames: A list containing the decoded frames' raw data. Only available if ReadFrames was true when reading the PPM file
# instance.Frames[frame] = [Inverted(boolean), [color #1, color #2], frame]
# color #1 and #2 is a value between 0 and 3, check the palette in instance.GetFrame() to see what they are.
# frame is a 3d list, used like this. frame[layer][x][y] = boolean
# To get a colored image of a frame, use instance.GetFrame()
# - instance.SoundData: A list contaning the raw data of the BGM and the 3 sound effects. Only available if ReadSound was true set to true
class PPM:
def __init__(self):
self.Loaded = [False, False, False]#(Meta, Frames, Sound)
self.Frames = None
self.Thumbnail = None
self.RawThumbnail = None
self.SoundData = None
def ReadFile(self, path, DecodeThumbnail=False, ReadFrames=True, ReadSound=False):#Load: (thumbnail, frames, sound)
f = open(path, "rb")
ret = self.Read(f.read(), DecodeThumbnail, ReadFrames, ReadSound)
f.close()
return ret
def Read(self, data, DecodeThumbnail=False, ReadFrames=True, ReadSound=False):#Load: (thumbnail, frames, sound)
if data[:4] <> "PARA" or len(data) <= 0x6a0:
return False
#Read the header:
AudioOffset = AscDec(data[4:8], True) + 0x6a0
AudioLenght = AscDec(data[8:12], True)
self.FrameCount = AscDec(data[12:14], True) + 1
self.Locked = ord(data[0x10]) & 0x01 == 1
self.ThumbnailFrameIndex = AscDec(data[0x12:0x14], True)#which frame the thumbnnail represents
self.OriginalAuthorName = data[0x14:0x2A].decode("UTF-16LE").split(u"\0")[0]
self.EditorAuthorName = data[0x2A:0x40].decode("UTF-16LE").split(u"\0")[0]
self.Username = data[0x40:0x56].decode("UTF-16LE").split(u"\0")[0]
self.OriginalAuthorID = data[0x56:0x5e][::-1].encode("HEX").upper()
self.EditorAuthorID = data[0x5E:0x66][::-1].encode("HEX").upper()#the last user to save the file
self.OriginalFilenameC = data[0x66:0x78]#compressed
self.CurrentFilenameC = data[0x78:0x8a]#compressed
self.OriginalFilename = "%s_%s_%s.tmb" % (self.OriginalFilenameC[:3].encode("HEX").upper(), self.OriginalFilenameC[3:-2], str(AscDec(self.OriginalFilenameC[-2:], True)).zfill(3))
self.CurrentFilename = "%s_%s_%s.tmb" % (self.CurrentFilenameC[:3].encode("HEX").upper(), self.CurrentFilenameC[3:-2], str(AscDec(self.CurrentFilenameC[-2:], True)).zfill(3))
self.PreviousEditAuthorID = data[0x8a:0x92][::-1].encode("HEX").upper()#don't know what this really is
#self.PartialFilenameC = data[0x92:0x9a]#compressed
self.Date = AscDec(data[0x9a:0x9e], True)#in seconds since midnight 1'st january 2000
self.RawThumbnail = data[0xa0:0x6a0]
if DecodeThumbnail:
self.GetThumbnail()#self.Thumbnail[x][y] = uint32 RGBA
self.Loaded[0] = True
#read the animation sequence header:
self.Looped = ord(data[0x06A6]) >> 1 & 0x01 == 1#Unverified?
AnimationOffset = 0x6a8 + AscDec(data[0x6a0:0x6a4], True)
FrameOffsets = [AnimationOffset + AscDec(data[0x06a8+i*4:0x06a8+i*4+4], True) for i in xrange(self.FrameCount)]
#Read the audio header:
self.SFXUsage = [(i&0x1<>0, i&0x2<>0, i&0x4<>0) for i in map(ord, data[AudioOffset:AudioOffset+self.FrameCount])]#SFXUsage[frame] = (sfx1, sfx2, sfx2) shere sfxX is either 0 or 1
SoundSize =(AscDec(data[AddPadding(AudioOffset+self.FrameCount, 4) :AddPadding(AudioOffset+self.FrameCount, 4)+ 4], True),#BG music
AscDec(data[AddPadding(AudioOffset+self.FrameCount, 4)+ 4:AddPadding(AudioOffset+self.FrameCount, 4)+ 8], True),#SFX1
AscDec(data[AddPadding(AudioOffset+self.FrameCount, 4)+ 8:AddPadding(AudioOffset+self.FrameCount, 4)+12], True),#SFX2
AscDec(data[AddPadding(AudioOffset+self.FrameCount, 4)+12:AddPadding(AudioOffset+self.FrameCount, 4)+16], True))#SFX3
self.Framespeed = 8 - ord(data[AddPadding(AudioOffset+self.FrameCount, 4) + 16])
self.BGMFramespeed = 8 - ord(data[AddPadding(AudioOffset+self.FrameCount, 4) + 17])#framespeed when the bgm was recorded
#Read the Frames:
if ReadFrames:
self.Frames = []#self.Frames[frame] = [inverted(bool), (color1(0-2), color2(0-2)), frame[layer][x][y] = bool]
for i, offset in enumerate(FrameOffsets):
#Read frame header:
Inverted = ord(data[offset]) & 0x01 == 0
#Reads which color that will be used:
Colors = (ord(data[offset]) >> 1 & 0x03,
ord(data[offset]) >> 3 & 0x03)
Frame = self.ExtractFrame(data, offset, self.Frames[i-1][2] if i else None)
self.Frames.append([Inverted, Colors, Frame])
self.Loaded[1] = True
else:
self.Loaded[1] = False
self.Frames = None
#Read the Audio:
if ReadSound:
self.SoundData = []
pos = AddPadding(AudioOffset+self.FrameCount+32, 4)
for i in xrange(4):
self.SoundData.append(data[pos:pos+SoundSize[i]])
pos += SoundSize[i]
self.Loaded[2] = True
else:
self.Loaded[2] = False
self.SoundData = None
#return the results
return self
def WriteFile(self, path):#not implented
pass
#Extracting:
def GetFrame(self, frame):#frame is the index in self.Frames. Only works if ReadFrames was True when reading the PPM file
if not self.Loaded[1]: return None
Inverted, Colors, Frame = self.Frames[frame]
#Defines the palette:
Palette = [0xFFFFFFFF,0x000000FF,0xFF0000FF,0x0000FFFF]
if Inverted:
Palette[0] = 0x000000FF
Palette[1] = 0xFFFFFFFF
Color1 = Palette[Colors[0]]
Color2 = Palette[Colors[1]]
out = []
for x in xrange(256):
out.append([])
for y in xrange(192):
if Frame[0][x][y]:#Color 1:
out[-1].append(Color1)
elif Frame[1][x][y]:#Color 2:
out[-1].append(Color2)
else:#background:
out[-1].append(Palette[0])
return out
def GetThumbnail(self, force=False):
if (not self.Thumbnail or force) and self.RawThumbnail:
if not self.RawThumbnail:
return False
out = [[0 for _ in xrange(48)] for _ in xrange(64)]
palette = (0xFEFEFEFF,#0
0x4F4F4FFF,#1
0xFFFFFFFF,#2
0x9F9F9FFF,#3
0xFF0000FF,#4
0x770000FF,#5
0xFF7777FF,#6
0x00FF00FF,#7-
0x0000FFFF,#8
0x000077FF,#9
0x7777FFFF,#A
0x00FF00FF,#B-
0xFF00FFFF,#C
0x00FF00FF,#D-
0x00FF00FF,#E-
0x00FF00FF)#F-
#8x8 tiling:
for ty in range(6):
for tx in range(8):
for y in range(8):
for x in range(0,8,2):
#two colors stored in each byte:
byte = ord(self.RawThumbnail[(ty*512+tx*64+y*8+x)/2])
out[x+tx*8 ][y+ty*8] = palette[byte & 0xF]
out[x+tx*8+1][y+ty*8] = palette[byte >> 4]
self.Thumbnail = out
return self.Thumbnail
#sub functions:
def ExtractFrame(self, data, offset, PrevFrame=None):
#defines line encoding storage:
#Enc1 = [0 for i in range(192)]
#Enc2 = [0 for i in range(192)]
#Defines the frame storage:
#Color1Frame = [[0 for i in range(192)] for i in range(256)]
#Color2Frame = [[0 for i in range(192)] for i in range(256)]
Encoding = [[], []]
Frame = [[[False for _ in xrange(192)] for _ in xrange(256)] for _ in xrange(2)]
#Read tags:
NewFrame = ord(data[offset]) & 0x80 <> 0
Unknown = ord(data[offset]) >> 5 & 0x03
offset += 1
#WIP - framemove:
FrameMove = [0,0]
if Unknown & 0x2:#doesn't work 100%...
move = AscDec(data[offset:offset+2], True)
if move > 128:
FrameMove[0] = move - 256
else:
FrameMove[1] = 0-move
offset += 2
if Unknown:
print "Unknown instance:",Unknown,"at offset ",offset-1
#read the line encoding of the layers:
for layer in xrange(2):
for byte in map(ord, data[offset:offset+48]):
Encoding[layer].append(byte & 0x03)
Encoding[layer].append(byte >> 2 & 0x03)
Encoding[layer].append(byte >> 4 & 0x03)
Encoding[layer].append(byte >> 6 )
offset += 48
#read layers:
for layer in xrange(2):
for y in xrange(192):
if Encoding[layer][y] == 0:#Nothing
pass
elif Encoding[layer][y] == 1:#Normal
UseByte = AscDec(data[offset:offset+4])
offset += 4
x = 0
while UseByte & 0xFFFFFFFF:
if UseByte & 0x80000000:
byte = ord(data[offset])
offset += 1
for _ in xrange(8):
if byte & 0x01:
Frame[layer][x][y] = True
x += 1
byte >>= 1
else:
x += 8
UseByte <<= 1
elif Encoding[layer][y] == 2:#Inverted
UseByte = AscDec(data[offset:offset+4])
offset += 4
x = 0
while UseByte&0xFFFFFFFF:
if UseByte & 0x80000000:
byte = ord(data[offset])
offset += 1
for _ in range(8):
if not byte & 0x01:
Frame[layer][x][y] = True
x += 1
byte >>= 1
else:
x += 8
UseByte <<= 1
for n in range(256):
Frame[layer][n][y] = not Frame[layer][n][y]
elif Encoding[layer][y] == 3:#Raw/full
x = 0
for _ in range(32):
byte = ord(data[offset])
offset += 1
for _ in range(8):
if byte & 0x01:
Frame[layer][x][y] = True
x += 1
byte >>= 1
#Merges this frame with the previous frame if NewFrame isn't true:
if not NewFrame and PrevFrame:
if FrameMove[0] or FrameMove[1]:#Moves the previus frame if specified:
NewPrevFrame = [[[False for _ in xrange(192)] for _ in xrange(256)] for _ in xrange(2)]
for y in range(192):
for x in range(256):
TempX = x+FrameMove[0]
TempY = y+FrameMove[1]
if 0 <= Tempx < 256 and 0 <= TempY < 192:
NewPrevFrame[0][TempX][TempY] = PrevFrame[0][x][y]
NewPrevFrame[1][TempX][TempY] = PrevFrame[1][x][y]
PrevFrame = NewPrevFrame
#Merge the frames:
for y in range(192):
for x in range(256):
Frame[0][x][y] = Frame[0][x][y] <> PrevFrame[0][x][y]
Frame[1][x][y] = Frame[1][x][y] <> PrevFrame[1][x][y]
return Frame
#Class TMB:
#
# With this class you can read a Flipnote TMB file into memory and get its attributes.
# You do so by using either instance.ReadFile() or instance.Read() of your instance of the TMB class.
# If you pass DecodeThumbnail=True to these functions, it will decode thumbnail.
# The TMB file is a file containing the metadata to a corresponding PPM file.
#
# After reading a TMB file, these values are available:
#
# - instance.FrameCount: The number of frames this flipnote consists of
# - instance.Locked: A boolean telling us wether it's locked or not
# - instance.ThumbnailFrameIndex: Which frame the thumbnail is(starting at 0)
# - instance.OriginalAuthorName: The name of the original author in unicode
# - instance.EditorAuthorName: The name of the (previous?) editing author in unicode
# - instance.Username: The username in unicode
# - instance.OriginalAuthorID: The ID of the original author
# - instance.EditorAuthorID: The ID of the last editor, the last user to save the file
# - instance.PreviousEditAuthorID: The ID of the previous editor
# - instance.OriginalFilename: The original filename
# - instance.CurrentFilename: The current filename
# - instance.Date: The date in seconds since January 1'st 2000
# - instance.GetThumbnail(): The thumbnail stored in a 2D list of uint32 RGBA values. Will decode it first if not done so already
class TMB:
def __init__(self):
self.Loaded = False
self.Thumbnail = None
self.RawThumbnail = None
def ReadFile(self, path, DecodeThumbnail=False):
f = open(path, "rb")
ret = self.Read(f.read(), DecodeThumbnail)
f.close()
return ret
def Read(self, data, DecodeThumbnail=False):
if data[:4] <> "PARA" or len(data) < 0x6a0:
return False
#Read the header:
self.AudioOffset = AscDec(data[4:8], True) + 0x6a0#only stored for self.Pack()
self.AudioLenght = AscDec(data[8:12], True)#only stored for self.Pack()
self.FrameCount = AscDec(data[12:14], True) + 1
self.Locked = ord(data[0x10]) & 0x01 == 1
self.ThumbnailFrameIndex = AscDec(data[0x12:0x14], True)#which frame is in the thumbnnail
#self.OriginalAuthorName = u"".join(unichr(AscDec(data[0x14+i*2:0x14+i*2+2], True)) for i in xrange(11)).split(u"\0")[0]
#self.EditorAuthorName = u"".join(unichr(AscDec(data[0x2A+i*2:0x2A+i*2+2], True)) for i in xrange(11)).split(u"\0")[0]
#self.Username = u"".join(unichr(AscDec(data[0x40+i*2:0x40+i*2+2], True)) for i in xrange(11)).split(u"\0")[0]
self.OriginalAuthorName = data[0x14:0x2A].decode("UTF-16LE").split(u"\0")[0]
self.EditorAuthorName = data[0x2A:0x40].decode("UTF-16LE").split(u"\0")[0]
self.Username = data[0x40:0x56].decode("UTF-16LE").split(u"\0")[0]
self.OriginalAuthorID = data[0x56:0x5e][::-1].encode("HEX").upper()
self.EditorAuthorID = data[0x5E:0x66][::-1].encode("HEX").upper()#the last user to save the file
self.OriginalFilenameC = data[0x66:0x78]#compressed
self.CurrentFilenameC = data[0x78:0x8a]#compressed
self.OriginalFilename = "%s_%s_%s.tmb" % (self.OriginalFilenameC[:3].encode("HEX").upper(), self.OriginalFilenameC[3:-2], str(AscDec(self.OriginalFilenameC[-2:], True)).zfill(3))
self.CurrentFilename = "%s_%s_%s.tmb" % (self.CurrentFilenameC[:3].encode("HEX").upper(), self.CurrentFilenameC[3:-2], str(AscDec(self.CurrentFilenameC[-2:], True)).zfill(3))
self.PreviousEditAuthorID = data[0x8a:0x92][::-1].encode("HEX").upper()#don't know what this really is
self.PartialFilenameC = data[0x92:0x9a]#compressed
self.Date = AscDec(data[0x9a:0x9e], True)#in seconds since midnight 1'st january 2000
self.RawThumbnail = data[0xa0:0x6a0]
if DecodeThumbnail:
self.GetThumbnail()#self.Thumbnail[x][y] = uint32 RGBA
self.Loaded = True
#return the results
return self
def WriteFile(self, path):#not implented
out = self.Pack()
if out:
f = open(path, "wb")
f.write(out)
f.close()
return True
else:
return False
def Pack(self, ppm=None):#not implented
if not self.Loaded: return False
#realself = self
#if ppm: self = ppm
out = ["PARA",#magic
DecAsc(self.AudioOffset-0x6a0, 4, True),#animation data size
DecAsc(self.AudioLenght, 4, True),#audio data size
DecAsc(self.FrameCount-1, 2, True),#frame count
"\x24\x00",#unknown
chr(self.Locked), "\0",#locked
DecAsc(self.ThumbnailFrameIndex, 2, True),#which frame is in the thumbnnail
self.OriginalAuthorName.encode("UTF-16LE") + "\0\0"*(11-len(self.OriginalAuthorName)),#Original Author Name
self.EditorAuthorName.encode("UTF-16LE") + "\0\0"*(11-len(self.EditorAuthorName)),#Editor Author Name
self.Username.encode("UTF-16LE") + "\0\0"*(11-len(self.Username)),#Username
self.OriginalAuthorID.decode("HEX")[::-1],#OriginalAuthorID
self.EditorAuthorID.decode("HEX")[::-1],#EditorAuthorID
self.OriginalFilenameC,#OriginalFilename
self.CurrentFilenameC,#CurrentFilename
self.PreviousEditAuthorID.decode("HEX")[::-1],#EditorAuthorID
self.PartialFilenameC,#PartialFilename
DecAsc(self.Date, 4, True),#Date in seconds
"\0\0",#padding
self.PackThumbnail()]#thumbnail
return "".join(out)
def GetThumbnail(self, force=False):
if (not self.Thumbnail or force) and self.RawThumbnail:
if not self.RawThumbnail:
return False
out = [[0 for _ in xrange(48)] for _ in xrange(64)]
palette = (0xFEFEFEFF,#0
0x4F4F4FFF,#1
0xFFFFFFFF,#2
0x9F9F9FFF,#3
0xFF0000FF,#4
0x770000FF,#5
0xFF7777FF,#6
0x00FF00FF,#7-
0x0000FFFF,#8
0x000077FF,#9
0x7777FFFF,#A
0x00FF00FF,#B-
0xFF00FFFF,#C
0x00FF00FF,#D-
0x00FF00FF,#E-
0x00FF00FF)#F-
#8x8 tiling:
for ty in range(6):
for tx in range(8):
for y in range(8):
for x in range(0,8,2):
#two colors stored in each byte:
byte = ord(self.RawThumbnail[(ty*512+tx*64+y*8+x)/2])
out[x+tx*8 ][y+ty*8] = palette[byte & 0xF]
out[x+tx*8+1][y+ty*8] = palette[byte >> 4]
self.Thumbnail = out
return self.Thumbnail
def PackThumbnail(self, Exact=True, force=False):#more or less a private function for now
palette = (0xFEFEFEFF,#0
0x4F4F4FFF,#1
0xFFFFFFFF,#2
0x9F9F9FFF,#3
0xFF0000FF,#4
0x770000FF,#5
0xFF7777FF,#6
0x00FF00FF,#7-
0x0000FFFF,#8
0x000077FF,#9
0x7777FFFF,#A
0x00FF00FF,#B-
0xFF00FFFF,#C
0x00FF00FF,#D-
0x00FF00FF,#E-
0x00FF00FF)#F-
if not self.Thumbnail:
return self.RawThumbnail
else:
if Exact:
out = []
#8x8 tiling:
for ty in range(6):
for tx in range(8):
for y in range(8):
for x in range(0,8,2):
#two colors stored in each byte:
#pos = 0xa0+(ty*512+tx*64+y*8+x)/2
p1 = palette.index(self.Thumbnail[x+tx*8 ][y+ty*8])
p2 = palette.index(self.Thumbnail[x+tx*8+1][y+ty*8])
out.append(chr(p2<<4 | p1))
self.RawThumbnail = "".join(out)
return self.RawThumbnail
else:
#not yet implented
return False
#Function WriteImage:
#
# Writes a 2D list if uint32 RGBA values as a image files.
# Designed to work with PPM().Thumbnail or PPM().GetFrame(n)
#
# This function requires the PIl imaging module
def WriteImage(image, outputPath):
if not hasPIL or not image: return False
out = []
for y in xrange(len(image[0])):
for x in xrange(len(image)):
out.append(DecAsc(image[x][y], 4))
out = Image.fromstring("RGBA", (len(image), len(image[0])), "".join(out))
filetype = outputPath[outputPath.rfind(".")+1:]
out.save(outputPath, filetype)
return True
#this is just for experimenting with sound decoding:
#not even close to being finished
def DecodeSound(outputpath, data):
f = wave.open(outputpath, "wb")
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(32000)
for i in data:
i1 = ord(i)&0xF
i2 = (ord(i)>>4)&0xF
#Noise reduction:
#if i1&0x8: i1 = 0xF-(i1&0x7)
#if i2&0x8: i2 = 0xF-(i2&0x7)
i1 = i1&0x7 if i1&0x8 else 0xF-i1
i2 = i2&0x7 if i2&0x8 else 0xF-i2
f.writeframes(DecAsc(i1<<12, 2, True)*4)
f.writeframes(DecAsc(i2<<12, 2, True)*4)
f.close()
#for testing during development:
if 1 == 2:
p = PPM()
print "loading ppm..."
p.ReadFile("PPMtests/test.ppm", (1, 1, 1))
print str(p.OriginalAuthorName), str(p.OriginalAuthorID)
print str(p.EditorAuthorName), str(p.EditorAuthorID)
print "Dumping Thumbnail..."
WriteImage(p.Thumbnail, "thumbnail.png")
print "Dumping frames..."
for i in xrange(p.FrameCount):
WriteImage(p.GetFrame(i), "frames/frame %i.png" % i)
print "Dumping raw sounds..."
for i, data in enumerate(p.SoundData):
f = open("sound %i.bin" % i, "wb")
f.write(data)
f.close()
print "Extracting sounds...."
for i, d in enumerate(p.SoundData): DecodeSound("out_%i.wav"%i, d)
if __name__ == '__main__':
print " == PPM.py =="
print " == by pbsds =="
print " == v1.03 =="
print
if len(sys.argv) < 3:
print "Usage:"
print " PPM.py <Mode> <Input> [<Output>] [<Frame>]"
print ""
print " <Mode>:"
print " 0: Extracts the thumbnail to the file <Output>"
print " 1: Extracts the frame(s) to <Output>"
print " 2: Dumps the raw sound data files to the folder <Output>"
print " 3: Same as mode 2, but will also dump the experimentally decoded"
print " sounds."
print " 4: Prints out the metadata. Can also write it to <output> which also"
print " supports unicode charactes."
print " Mode 0 and 4 supports TMB files aswell"
print " <Frame>"
print " Only used in mode 1"
print " Set this to the exact frame you want to extract(starting at 1) and it"
print " will be saved as a file to <Output>."
print " If not specified, it will extract all frames to the folder <Output>"
sys.exit()
import os, time
if sys.argv[1] == "0":
print "Reading the flipnote file...",
if not os.path.isfile(sys.argv[2]):
print "Error!\nSpecified file doesn't exist!"
sys.exit()
flipnote = TMB().ReadFile(sys.argv[2], True)
if not flipnote:
print "Error!"
print "The given file is not a Flipnote PPM file or TMB file!"
sys.exit()
print "Done!"
print "Dumping the thumbnail...",
WriteImage(flipnote.GetThumbnail(), sys.argv[3])
print "Done!"
elif sys.argv[1] == "1":
if len(sys.argv) < 4:
print "Error!"
print "<Output> not specified!"
sys.exit()
print "Reading the flipnote file...",
if not os.path.isfile(sys.argv[2]):
print "Error!\nSpecified file doesn't exist!"
sys.exit()
flipnote = PPM().ReadFile(sys.argv[2])
if not flipnote:
print "Error!\nThe given file is not a Flipnote PPM file."
sys.exit()
print "Done!"
if len(sys.argv) < 5:
if not os.path.isdir(sys.argv[3]):
print "Error!\nThe specified directory doesn't exist!"
sys.exit()
for i in xrange(flipnote.FrameCount):
print "Dumping frame #%i..." % (i+1),
WriteImage(flipnote.GetFrame(i), os.path.join(sys.argv[3], "frame %s.png" % str(i+1).zfill(3)))
print "Done!"
else:
try:
int(sys.argv[4])
except:
print "Error!\nInvalid <Frame>!"
sys.exit()
if not (0 <= int(sys.argv[4])-1 < flipnote.FrameCount):
print "Error!\n<Frame> is out of bounds!"
sys.exit()
print "Dumping frame #%i..." % int(sys.argv[4]),
WriteImage(flipnote.GetFrame(int(sys.argv[4])-1), sys.argv[3])
print "Done!"
elif sys.argv[1] in "23":
if len(sys.argv) < 4:
print "Error!"
print "<Output> not specified!"
sys.exit()
print "Reading the flipnote file...",
if not os.path.isfile(sys.argv[2]):
print "Error!\nSpecified file doesn't exist!"
sys.exit()
flipnote = PPM().ReadFile(sys.argv[2], ReadFrames=False, ReadSound=True)
if not flipnote:
print "Error!\nThe given file is not a Flipnote PPM file."
sys.exit()
print "Done!"
if not os.path.isdir(sys.argv[3]):
print "Error!\nThe specified directory doesn't exist!"
sys.exit()
print "Dumping the raw sound data...",
for i, data in enumerate(flipnote.SoundData):
f = open(os.path.join(sys.argv[3], ("BGM.bin", "SFX1.bin", "SFX2.bin", "SFX3.bin")[i]), "wb")
f.write(data)
f.close()
print "Done!"
if sys.argv[1] == "3":
print "Dumping the decoded sound data...",
for i, data in enumerate(flipnote.SoundData):
DecodeSound(os.path.join(sys.argv[3], ("BGM decoded.wav", "SFX1 decoded.wav", "SFX2 decoded.wav", "SFX3 decoded.wav")[i]), data)
print "Done!"
print "Dumping the sound effect usage...",
f = open(os.path.join(sys.argv[3], "SFX usage.txt"), "w")
for i, (s1, s2, s3) in enumerate(flipnote.SFXUsage):
f.write("Frame %i:%s%s%s\n" % (i, " SFX1"*s1, " SFX2"*s2, " SFX3"*s3))
f.close()
print "Done!"
elif sys.argv[1] == "4":
epoch = time.mktime(time.struct_time([2000, 1, 1, 0, 0, 0, 5, 1, -1]))
if not os.path.isfile(sys.argv[2]):
print "Error!\nSpecified file doesn't exist!"
sys.exit()
filetype = "ppm" if sys.argv[2][-3:] == "ppm" else "tmb"
flipnote = TMB().ReadFile(sys.argv[2]) if filetype == "tmb" else PPM().ReadFile(sys.argv[2], ReadFrames=False)
if not flipnote:
print "Error!\nThe given file is not a Flipnote PPM file or TMB file."
if len(sys.argv) >= 4:
f = open(sys.argv, "wb")
f.write("Error!\nThe given file is not a Flipnote PPM file or TMB file.")
f.close()
sys.exit()
meta = []
meta.append(u"Current filename: %s" % flipnote.CurrentFilename[:-3]+filetype)
meta.append(u"Original filename: %s" % flipnote.OriginalFilename[:-3]+filetype)
meta.append(u"Number of frames: %s" % flipnote.FrameCount)
meta.append(u"Locked: %s" % flipnote.Locked)
if filetype == "ppm":
meta.append(u"Frame speed: %s" % flipnote.Framespeed)
meta.append(u"Looped: %s" % flipnote.Looped)
meta.append(u"Thumbnail frame index: %i" % (flipnote.ThumbnailFrameIndex+1))
meta.append(u"Original author: %s" % flipnote.OriginalAuthorName)
meta.append(u"Editor author: %s" % flipnote.EditorAuthorName)
meta.append(u"Username: %s" % flipnote.Username)
meta.append(u"Original author ID: %s" % flipnote.OriginalAuthorID)
meta.append(u"Editor author ID: %s" % flipnote.EditorAuthorID)
meta.append(u"Date(seconds since 1'st Jan 2000): %s" % flipnote.Date)
meta.append(time.strftime(u"Date: %H:%M %d/%m-%Y", time.gmtime(epoch+flipnote.Date)) + " (faulty)")
newline = "\n"
if sys.platform in ("win32", "cygwin"): newline = "\r\n"
elif sys.platform in ("darwin"): newline = "\r"
print newline.join(meta).encode('ascii', 'ignore')
if len(sys.argv) >= 4:
f = open(sys.argv[3], "wb")
f.write(newline.join(meta).encode("UTF-8"))
f.close()
else:
print "Error!\nThere's no such mode."
+30
View File
@@ -0,0 +1,30 @@
== Hatenatools ==
== By pbsds ==
== aka Peder Bergebakken Sundt ==
--VERSIONS--
PPM.py: v1.03
UGO.py: v0.09
NTFT.py: v0.05
--DESCRIPTION--
PPM.py can read the metadata, thumbnail, frames and the sound of Flipnote Studio files: .ppm
It can also handle .tmb files.
UGO.py can read and write UGO files, and modify their content. It can also convert UGO files
to and from the XML format
NTFT.py can read ntft image files and convert them to image files
Warning, it's still in a very crude state. Error will occour
These are all tested and written in python 2.7
These are all designed to be usable both as python modules and as stand alone applications.
--LICENCE--
Free to use.
They are free to modify as you wish as long you give credit to me, pbsds / Peder Bergebakken Sundt
--CREDIT--
-Steven for most of the documentation on DSiBrew and his frame decoding example on his talkpage
-Remark for helping me understanding the 8x8 tiling on the preview images.
-Austin Burk and Jsafive for supplying .tmb and .ugo files
+573
View File
@@ -0,0 +1,573 @@
#This class reads and writes UGO files.
#cal also export and import it in the xml format
#Extended functionality added by PPM.py from http://pbsds.net/projects/ppm
import sys, os
from base64 import b64encode, b64decode
import xml.etree.ElementTree as ET
try:
import PPM
HasPPM = True
except ImportError:
HasPPM = False
#helpers:
def AscDec(ascii, LittleEndian=False):#Converts a ascii string into a decimal
ret = 0
l = map(ord, ascii)
if LittleEndian: l.reverse()
for i in l:
ret = (ret<<8) | i
return ret
def DecAsc(dec, length=None, LittleEndian=False):#Converts a decimal into an ascii string of chosen length
out = []
while dec <> 0:
out.insert(0, dec&0xFF)
dec >>= 8
#"".join(map(chr, out))
if length:
if len(out) > length:
#return "".join(map(chr, out[-length:]))
out = out[-length:]
if len(out) < length:
#return "".join(map(chr, [0]*(length-len(out)) + out))
out = [0]*(length-len(out)) + out
if LittleEndian: out.reverse()
return "".join(map(chr, out))
def zipalign(length, r=4):
return length + (4 - length % r) if length % r else length
def indentXML(elem, level=0):#borrowed from: http://effbot.org/zone/element-lib.htm#prettyprint
i = "\n" + level*"\t"
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + "\t"
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indentXML(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
#class UGO:
class UGO:
def __init__(self):
self.Loaded = False
def ReadFile(self, path):
f = open(path, "rb")
ret = self.Read(f.read())
f.close()
return ret
def Read(self, data):
#Filestructure:
# - Header
# - Table of Contents
# - Padding, normally of length 2
# - Extra Data
# - Padding
#The padding seems to do zipaligning to a length of 4
global HasPPM
#Header:
if data[:4] <> "UGAR": return False#The file isn't a UGO file
Sections = AscDec(data[ 4: 8], True)#could also be version
if Sections >= 1:
self.TableLength = AscDec(data[ 8:12], True)
if Sections >= 2:
self.ExtraLength = AscDec(data[12:16], True)
if Sections > 2:
print "Warning: This UGO file has more than the 2 known sections:",Sections
print "Please send this UGO file to pbsds over at pbsds.net"
print "This file could possibly be read incorrectly..."
headerlength = 8 + Sections*4
#Read table of contents:
#A table where the rows are seperated with newlines, and colons with tabs
if Sections >= 1:
TableOfContents = tuple((i.split("\t") for i in data[headerlength:headerlength+self.TableLength].split("\n")))
else:
TableOfContents = []
#Extra data:
if Sections >= 2:
ExtraData = data[zipalign(headerlength+self.TableLength) : zipalign(headerlength+self.TableLength)+self.ExtraLength]
#Read data:
self.Items = []
pos = 0#Extra Data position
tmbcount = 1#used if HasPPM is False
ntftcount = 1#used if no label
names = []
for i in TableOfContents:
type = int(i[0])
if type == 0:#layout
#I've always seen just numbers here.
#The amount of numbers also differ.
#maybe related to color scheme? or the button scheme/layout
#all pages containing TMBs have so far been: i = ["0", "2", "1"]
self.Items.append(("layout", map(int, i[1:])))
continue
elif type == 1:#Text on topscreen
num = int(i[1])#always seen as 0, unknown purpose
labels = [b64decode(i[n]).decode("UTF-16LE") for n in xrange(2,7)]#5 labels probably one label for each line of text
self.Items.append(("topscreen text", labels, num))
continue
elif type == 2:#catogories(like "reccomended" and "new flipnotes" and "most popular")
#this one may change greatly depending on what layout is set in type==0
link = i[1]
label = b64decode(i[2]).decode("UTF-16LE")
selected = int(i[3]) <> 0#bool
self.Items.append(("category", link, label, selected))
continue
elif type == 3:#POST button? "Post Flipnote"
pass
elif type == 4:#Button
link = i[1]
trait = int(i[2])
label = b64decode(i[3]).decode("UTF-16LE")
other = i[4:]#varies
#extra data
file = None#== ("filename", "filedata")
if trait < 100 and ExtraData:
if ExtraData == "\x20":#empty
pass#no files
elif ExtraData[pos:pos+4] == "PARA":#tmb file
file = ExtraData[pos:pos+0x6A0]
pos += 0x6A0
if HasPPM:
tmb = PPM.TMB().Read(file)
name = tmb.CurrentFilename[:-4]
del tmb
else:
name = "embedded tmb #%i" % tmbcount
tmbcount += 1
if name+".tmb" in names:
j = 2
while "%s_%i.tmb" % (name, j) not in names:
j += 1
name = "%s_%i" % (name, j)
file = (name+".tmb", file)
names.append(name+".tmb")
else:#ntft icon
name = label.encode("ascii", "ignore")
if not name:
name = "nameless ntft %i" % ntftcount
ntftcount += 1
if name+".ntft" in names:
j = 2
while "%s_%i.ntft" % (name, j) not in names:
j += 1
name = "%s_%i" % (name, j)
file = (name+".ntft", ExtraData[pos:pos+2048])
names.append(name+".ntft")
pos += 2048
self.Items.append(("button", trait, label, link, other, file))
# if subtype == 3:#flipnote
# tmb = PPM.TMB().Read(ExtraData[pos:pos+0x6A0]); pos += 0x6A0
# unknown1 = i[3]#empty
# stars = int(i[4])#not sure
# unknown2 = map(int, i[5:8])#unknown = [765, 573, 0]
# self.Items.append(("flipnote", link, tmb, stars, unknown1, unknown2))
# continue
# elif subtype == 100: pass
# elif subtype == 101: pass
# elif subtype == 102: pass
# elif subtype == 104:#list item? like mails and announcements
# label = b64decode(i[3]).decode("UTF-16LE")
# unknown = i[4]
# num = int(i[5])#only seen as 0
# # self.Items.append(("list item?", link, label, unknown, num))
# # continue
# pass
# elif subtype == 115:#Labeled button link(size of a flipnote thumbnail, commonly "next page")
# label = b64decode(i[3]).decode("UTF-16LE")
# unknown = i[4:6]
# self.Items.append(("thumbnail link", link, label, unknown))
# continue
# elif subtype == 117: pass
continue
#if not recognized:
self.Items.append(("unknown", i))
print "Unknown UGO item discovered:", i
self.Loaded = True
return self
def WriteFile(self, path):
if self.Loaded:
out = self.Pack()
if out:
f = open(path, "wb")
f.write(out)
f.close()
return True
else:
return False
def Pack(self):
if not self.Loaded: return False
Header = ["UGAR", None]
TableOfContents = []
ExtraData = []
#Encode data:
for i in self.Items:
if i[0] == "unknown":
TableOfContents.append("\t".join(i[1]))
elif i[0] == "layout":#0
TableOfContents.append("\t".join(["0"] + map(str, list(i[1]))))
elif i[0] == "topscreen text":#1
labels, num = i[1:]
num = str(num)
for i in xrange(5):
labels[i] = b64encode(labels[i].encode("UTF-16LE"))
TableOfContents.append("\t".join(("1", num, labels[0], labels[1], labels[2], labels[3], labels[4])))
elif i[0] == "category":#2
link, label, selected = i[1:]
label = b64encode(label.encode("UTF-16LE"))
selected = str(1*selected)
TableOfContents.append("\t".join(("2", link, label, selected)))
elif i[0] == "post":#not yet implented
pass
elif i[0] == "button":
trait, label, link, other, file = i[1:]
trait = str(trait)
label = b64encode(label.encode("UTF-16LE"))
TableOfContents.append("\t".join(["4", link, trait, label] + list(other)))
if file:
ExtraData.append(file[1])
else:
print "Unrecognized entry in self.Items:", i
TableOfContents = "\n".join(TableOfContents)
ExtraData = "".join(ExtraData)
#Format data:
Sections = 0
if TableOfContents:
Sections += 1
Header.append(DecAsc(len(TableOfContents), 4, True))
#padding/zipaligning
if len(TableOfContents) % 4:
TableOfContents += "\0" * (4 - len(TableOfContents) % 4)
if ExtraData:
Sections += 1
Header.append(DecAsc(len(ExtraData), 4, True))
#padding/zipaligning
if len(ExtraData) % 4:
ExtraData += "\0" * (4 - len(ExtraData) % 4)
Header[1] = DecAsc(Sections, 4, True)
#Zip up and send the file:
return "".join(Header) + TableOfContents + ExtraData
#XML
def WriteXML(self, xmlname="content.ugoxml", folder="content.ugoxml embedded"):#WIP
if not self.Loaded: return False
path, xmlname = os.path.split(xmlname)
ugo_xml = ET.Element("ugo_xml")
files = []
for i in self.Items:
if i[0] == "unknown":
elem = ET.SubElement(ugo_xml, "unknown", type=i[1][0])
for value in i[1][1:]:
ET.SubElement(elem, "value").text = value
continue
elif i[0] == "layout":#0
elem = ET.SubElement(ugo_xml, "layout")
for value in i[1]:
ET.SubElement(elem, "value").text = str(value)
continue
elif i[0] == "topscreen text":#1
elem = ET.SubElement(ugo_xml, "title")
labels, num = i[1:]
for label in labels:
ET.SubElement(elem, "label").text = label
ET.SubElement(elem, "num").text = str(num)
continue
elif i[0] == "category":#2
elem = ET.SubElement(ugo_xml, "category")
link, label, selected = i[1:]
ET.SubElement(elem, "label").text = label
ET.SubElement(elem, "address").text = link
ET.SubElement(elem, "selected").text = str(selected).lower()
elif i[0] == "post":#3
pass#not yet implented/parsed
elif i[0] == "button":#4
elem = ET.SubElement(ugo_xml, "button")
trait, label, link, other, file = i[1:]
ET.SubElement(elem, "label").text = label
ET.SubElement(elem, "address").text = link
ET.SubElement(elem, "trait").text = str(trait)#todo: add names
for n, value in enumerate(other):
entry = ET.SubElement(elem, "value")
entry.text = value
if n == 0 and trait == 3:
entry.attrib["tip"] = "stars"
if file:
ET.SubElement(elem, "embedded_file").text = os.path.join(folder, file[0])
files.append((os.path.join(folder, file[0]), file[1]))
#intend
indentXML(ugo_xml)
#pack xml
files.append((xmlname, ET.tostring(ugo_xml, encoding="UTF-8")))
#write files
if not os.path.isdir(os.path.join(path, folder)):
os.mkdir(os.path.join(path, folder))
for name, data in files:
f = open(os.path.join(path, name), "wb")
f.write(data)
f.close()
def ReadXML(self, xmlfile, silent=True):
ugo_xml = ET.parse(xmlfile).getroot()
xmlpath = os.path.split(xmlfile)[0]
Items = []
for elem in ugo_xml:
if elem.tag == "unknown":
if "type" not in elem.attrib:
if not silent: print "Invalid formatting. <unknown> without \"type\" attribute"
return False
values = [elem.attrib["type"]]
for value in elem:
if value.tag <> "value":
if not silent: print "Invalid formatting. <%s> found within <unknown>" % value.tag
return False
values.append(value.text if value.text else "")
Items.append(("unknown", values))
elif elem.tag == "layout":#0
values = []
for value in elem:
if value.tag <> "value":
if not silent: print "Invalid formatting. <%s> found within <layout>" % value.tag
return False
if not value.text.isdigit():
if not silent: print "Invalid entry. <value> in <layout> is not a number" % value.tag
return False
values.append(int(value.text))
Items.append(("layout", values))
elif elem.tag == "title":#1
labels = ["", "", "", "", ""]
num = 0
pos = 0
numset = False
for value in elem:
if value.tag not in ("label", "num"):
if not silent: print "Invalid formatting. <%s> found within <title>" % value.tag
return False
if value.tag == "label":
if pos >= 5:
if not silent: print "Invalid formatting. More than 5 <labels> in <title>"
return False
if value.text: labels[pos] = value.text
pos += 1
elif value.tag == "num":
if numset:
if not silent: print "Invalid formatting. Multible <num> in <title>"
return False
if not value.text.isdigit():
if not silent: print "Invalid entry. <num> in <title> is not a number!"
return False
num = int(value.text)
numset = True
Items.append(("topscreen text", labels, num))
elif elem.tag == "category":#2
link = None
label = None
selected = None
for value in elem:
if value.tag not in ("label", "address", "selected"):
if not silent: print "Invalid formatting. <%s> found within <category>" % value.tag
return False
if value.tag == "address":
if isinstance(link, str):
if not silent: print "Invalid formatting. multible <address> within <category>"
return False
link = value.text if value.text else ""
elif value.tag == "label":
if isinstance(label, str):
if not silent: print "Invalid formatting. multible <label> within <category>"
return False
label = value.text if value.text else ""
elif value.tag == "selected":
if selected in (True, False):
if not silent: print "Invalid formatting. multible <selected> within <category>"
return False
selected = value.text[0].lower() in "t1"
Items.append(("category", link, label, selected))
elif elem.tag == "post":#3 not yet implented
pass
elif elem.tag == "button":#4
trait = None#todo: add names
label = None
link = None
other = []
file = None
for value in elem:
if value.tag not in ("label", "address", "trait", "value", "embedded_file"):
if not silent: print "Invalid formatting. <%s> found within <button>" % value.tag
return False
if value.tag == "label":
if isinstance(label, str):
if not silent: print "Invalid formatting. Multible <label> within <button>"
return False
label = value.text if value.text else ""
elif value.tag == "address":
if isinstance(link, str):
if not silent: print "Invalid formatting. Multible <address> within <button>"
return False
link = value.text if value.text else ""
elif value.tag == "trait":#todo: add names
if isinstance(trait, str):
if not silent: print "Invalid formatting. Multible <trait> within <button>"
return False
if not value.text.isdigit():
if not silent: print "Invalid entry. <trait> in <button> is not a number"
return False
trait = int(value.text)
elif value.tag == "value":
other.append(value.text if value.text else "")
elif value.tag == "embedded_file":
if file <> None:
if not silent: print "Invalid formatting. Multible <embedded_file> within <button>"
return False
path = os.path.join(xmlpath, value.text)
if not os.path.isfile(path):
if not silent: print "Invalid entry. Embedded file \"%s\" not found!" % value.text
print path
return False
f = open(path, "rb")
file = (os.path.split(value.text)[1], f.read())
f.close()
if None in (trait, label, link):
if not silent: print "Invalid formatting. <button> lacks either a <trait>, a <address> or a <label>"
return False
Items.append(("button", trait, label, link, other, file))
else:
if not silent:
print "Invalid formatting: <%s> found within <ugo_xml>" % elem.tag
self.Items = Items
self.Loaded = True
return self
if __name__ == "__main__":
print " == UGO.py =="
print " == by pbsds =="
print " == v0.90 =="
print
if len(sys.argv) < 3:
print "Usage:"
print " UGO.py <mode> <input> [<output> [<foldername>]]"
print ""
print " <Mode>:"
print " -d: Converts the UGO file in <input> to a UGOXML file with the same"
print " name, unless <output> is specified. Any embedded files will be"
print " written to a folder called UGOXML-filename + \" embedded\" unless"
print " <foldername> is given."
print " <foldername> is relative to the XML."
print " -e: Converts the UGOXML file in <input> to a UGO file with the same"
print " name, unless <output> is specified."
sys.exit()
mode = sys.argv[1]
if mode not in ("-d", "-e"):
print "Invalid <mode> given"
sys.exit()
if mode == "-d":
input = sys.argv[2]
output = sys.argv[3] if len(sys.argv) >= 4 else sys.argv[2]+"xml"
foldername = sys.argv[4] if len(sys.argv) >= 5 else os.path.split(output)[1] + " embedded"
print "Reading %s..." % os.path.split(input)[1]
ugo = UGO().ReadFile(input)
if not ugo:
print "Error!\n The given file is not a UGO file!"
sys.exit()
print "Done!"
print "Writing XML..."
ugo.WriteXML(output, foldername)
print "Done!\n\nHave a nice day!"
if mode == "-e":
input = sys.argv[2]
output = sys.argv[3] if len(sys.argv) >= 4 else ".".join(sys.argv[2].split(".")[:-1]) + ".ugo"
print "Reading %s..." % os.path.split(input)[1]
try:
ugo = UGO().ReadXML(input, False)
except EL.ParseError:
print "Error!\nThe given file is not in the XML format"
ugo = False
if not ugo:
#it prints sufficient errormessages
#print "Error!\n The given file is not a UGO file!"
sys.exit()
print "Done!"
print "Writing UGO..."
ugo.WriteFile(output)
print "Done"
+3
View File
@@ -0,0 +1,3 @@
from PPM import PPM, TMB
from UGO import UGO
from NTFT import NTFT
+5
View File
@@ -0,0 +1,5 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2
[InternetShortcut]
URL=http://pbsds.net/projects/hatenatools
IDList=