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=
@@ -0,0 +1 @@
74CC1F_0D376FC60C9DC_000 1 5 0 0 0 0
+1
View File
@@ -0,0 +1 @@
913304C0CC74CC1F 74CC1F_0D376FC60C9DC_000
+325
View File
@@ -0,0 +1,325 @@
from twisted.web import static, resource
from twisted.internet import reactor
import sys, os, atexit, glob, imp
from Hatenatools import *#UGO, PPM, TMB and NTFT
ServerLog = None#Is set to the log class by server.py
Silent = False
def Log(request, path=None):
if not path:
path = "\"%s\"" % request.path
ServerLog.write("%s requested %s" % (request.getClientIP(), path), Silent)
#Responses:
class AccessDenied(resource.Resource):
isLeaf = True
def render(self, request):
ServerLog.write("%s got 403 when requesting \"%s\"" % (request.getClientIP(), request.path), Silent)
request.setResponseCode(403)
return "403 - Access denied\nThis proxy is only for Flipnote Hatena for the DSi."
AccessDenied = AccessDenied()
class NotFound(resource.Resource):
isLeaf = True
def render(self, request):
args = "&".join(("%s=%s" % (i, request.args[i][0]) for i in request.args))
path = ("%s?%s" % (request.path, args)) if args else request.path
ServerLog.write("%s got 404 when requesting \"%s\"" % (request.getClientIP(), path), Silent)
request.setResponseCode(404)
return "404 - Not Found\nThis proxy is only for Flipnote Hatena for the DSi."
NotFound = NotFound()
#Root structure:
class Root(resource.Resource):#filters out non-hantena clients
isLeaf = False
def __init__(self):
resource.Resource.__init__(self)
self.dsResource = ds()
self.cssResource = static.File("hatenadir/css/")
self.imagesResource = static.File("hatenadir/images/")
def getChild(self, name, request):
if "x-dsi-sid" not in request.getAllHeaders():
return AccessDenied
if name == "ds":
return self.dsResource
elif name == "css":
return self.cssResource
elif name == "images":
return self.imagesResource
elif name == "":
return self
#return NotFound
return self
def render(self, request):
Log(request, "root")
return "Welcome to hatena.pbsds.net!\nThis is in early stages, so please don't expect too much."
class ds(resource.Resource):#child of Root
isLeaf = False
def __init__(self):
resource.Resource.__init__(self)
self.region = UgoRoot()
self.regions = ("v2-xx", "v2-eu", "v2-us", "v2-jp")
def getChild(self, name, request):
if name in self.regions:
return self.region
elif name == "":
return self
return NotFound
def render(self, request):
Log(request)
return "ds desu"
class UgoRoot(resource.Resource):#child of ds. (v2-xx)
isLeaf = False
def __init__(self):
resource.Resource.__init__(self)
LoadHatenaStructure(self)
def getChild(self, name, request):
if name == "":
return self
return NotFound
def render(self, request):
Log(request, "ugoroot")
return "UgoRoot desu"
#UgoRoot filestructure:
class FileResource(resource.Resource):
isLeaf = True
def __init__(self, filepath, Store=False):
resource.Resource.__init__(self)
self.Store = Store
if Store:
f = open(filepath, "rb")
self.file = f.read()
f.close()
else:
self.file = filepath
self.html = filepath.split(".")[-1][:3] == "htm"
def render(self, request):
path = "/".join(request.path.split("/")[3:])
Log(request, path)
if self.html:
#request.responseHeaders.setRawHeaders('content-type', ['text/html'])
pass
else:
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
if self.Store:
return self.file
else:
#f = open(self.file, "rb")
#ret = f.read()
#f.close()
#return ret
return static.File(self.file).render(request)
class UGOXMLResource(resource.Resource):
isLeaf = True
def __init__(self, filepath):
resource.Resource.__init__(self)
self.ugofile = UGO().ReadXML(filepath, False)
self.ugofile = self.ugofile.Pack()
def render(self, request):
path = "/".join(request.path.split("/")[3:])
Log(request, path)
#page = int(request.args["page"][0]) if "page" in request.args else 1
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return self.ugofile
class FolderResource(resource.Resource):
isLeaf = False
def getChild(self, name, request):
if name == "":
return self
return NotFound
def render(self, request):
path = "/".join(request.path.split("/")[3:])
Log(request, path)
return "This is a folder, but I'm to lazy to list the contents..."
def LoadHatenaStructure(Resource, path="hatenadir/ds/v2-xx", root = True):
for i in glob.glob("%s\*" % path):
if os.path.isfile(i):
filename = os.path.basename(i)
filetype = filename.split(".")[-1].lower()
if filetype == "ugoxml":
Resource.putChild(filename[:-3], UGOXMLResource(i))
elif filetype == "py":
try:
#sys.path.append(os.path.abspath(os.path.dirname(i)))
# #pyfile = __import__(filename[:-3])
#pyfile = importlib.import_module(filename[:-3])
#sys.path.pop(-1)
#pyfile = imp.load_module(i)
pyfile = imp.load_source("pyfile", os.path.abspath(i))
except ImportError as err:
pyfile = None
print "Error!"
print "Failed to import the python file \"%s\"" % i
print err
if pyfile:
Resource.putChild(filename[:-3], pyfile.PyResource())
elif filetype == "pyc":
pass#ignore
else:
Resource.putChild(filename, FileResource(os.path.join(path, filename)))
elif os.path.isdir(i):
if os.path.basename(i)[:2] <> "__":
folder = FolderResource()
LoadHatenaStructure(folder, os.path.join(path, os.path.basename(i)), False)
Resource.putChild(os.path.basename(i), folder)
#Flipnote handler:
class Database:
def __init__(self):
#read:
f = open("database/new_flipnotes.dat", "rb")#contains the newest 5000 flipnotes
self.Newest = [tuple(i.split("\t")) for i in f.read().split("\n")]#[i] = [creatorID, filename]
self.new = False#True when new flipnotes has been uploaded
f.close()
self.Creator = {}#to store creator info updates before writing to disk. Creator[id][n] = [filename, views, stars, green stars, red stars, blue stars, purple stars, Channel]
reactor.callLater(60*3, self.flusher)
atexit.register(self.write)
#to check if an update is neccesary:
self.Views = 0
self.Stars = 0
def flusher(self):#Automatically flushes the files every 3 minutes and trims down memory usage
reactor.callLater(60*3, self.flusher)
#trim newest:
if len(self.Newest) > 5000:
self.Newest = self.Newest[:5000]
self.write()
def write(self):
if self.new:
f = open("database/new_flipnotes.dat", "wb")
f.write("\n".join(("\t".join(i) for i in self.Newest)))
f.close()
self.new = False
for ID in self.Creator.keys():
f = open("database/Creators/%s/flipnotes.dat" % ID, "wb")
f.write("\n".join(("\t".join(map(str, i)) for i in self.Creator[ID])))
f.close()
del self.Creator[ID]
#helpers:
def FlipnotePath(self, CreatorID, filename):
return "database/Creators/%s/%s" % (CreatorID, filename)
def CreatorExists(self, CreatorID):
return os.path.exists("database/Creators/" + CreatorID) or (CreatorID in self.Creator)
def FlipnoteExists(self, CreatorID, filename):
return os.path.exists("database/Creators/%s/%s.ppm" % (CreatorID, filename))
#interface:
def GetCreator(self, CreatorID, Load=False):#Load holds it in memory, use this when making changes or reading him often
if CreatorID in self.Creator:
return self.Creator[CreatorID]
else:
if not os.path.exists("database/Creators/" + CreatorID):
return None
f = open("database/Creators/%s/flipnotes.dat" % CreatorID, "rb")
ret = [i.split("\t") for i in f.read().split("\n")]
f.close()
if Load:
self.Creator[CreatorID] = ret
return ret
def GetFlipnote(self, CreatorID, filename):#[filename, views, stars, green stars, red stars, blue stars, purple stars, Channel]
for i in (self.GetCreator(CreatorID) or []):
if i[0] == filename:
return i
return False
def AddFlipnote(self, content, Channel=""):
tmb = TMB().Read(content)
if not tmb:
return False
#CreatorID = tmb.Username
CreatorID = tmb.EditorAuthorID
filename = tmb.CurrentFilename[:-4]
del tmb
if os.path.exists("database/Creators/%s/%s.ppm" % (CreatorID, filename)):#already exists
return False
#add to database:
self.new = True
self.Newest.insert(0, (CreatorID, filename))
if not self.GetCreator(CreatorID, True):
self.Creator[CreatorID] = [[filename, 0, 0, 0, 0, 0, 0, Channel]]
else:
self.Creator[CreatorID].append([filename, 0, 0, 0, 0, 0, 0, Channel])
#write flipnote to file:
if not os.path.isdir("database/Creators/" + CreatorID):
os.mkdir("database/Creators/" + CreatorID)
f = open("database/Creators/%s/%s.ppm" % (CreatorID, filename), "wb")
f.write(content)
f.close()
return CreatorID, filename
def AddView(self, CreatorID, filename):
for i, flipnote in enumerate(self.GetCreator(CreatorID, True) or []):
if flipnote[0] == filename:
self.Creator[CreatorID][i][1] = int(flipnote[1]) + 1
self.Views += 1
return True
return False
def AddStar(self, Creator, filename, amount=1):
for i, flipnote in enumerate(self.GetCreator(CreatorID, True) or []):
if flipnote[0] == filename:
self.Creator[CreatorID][i][2] = int(flipnote[2]) + amount
self.Stars += 1
return True
return False
Database = Database()
#when loading:
def Setup():
print "Setting up hatena...",
root = Root()
print "Done!"
return root
+830
View File
@@ -0,0 +1,830 @@
@charset "UTF-8";
table.profbox {
background-color: #82c300;
color: #ffffff;
}
div.prof_out {
margin: 0 11px 0 11px;
background-color: #82c300;
border-style: solid;
border-width: 0 1px 0 1px;
border-color: #82c300;
}
span.more-f{
color: #ffffff;
}
div.notice2 {
margin:0 11px 5px 11px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#82c300;
background-color:#fbfbfb;
}
span.url{
text-decoration:underline;
}
span.comptxt {
color:#ff8a00;
}
span.star0 {
color:#fdc556;
}
span.star1 {
color:#138200;
}
span.star2 {
color:#ff455c;
}
span.star3 {
color:#00b0f9;
}
span.star4 {
color:#a411b9;
}
span.star0c {
color:#fdc556;
}
span.star1c {
color:#138200;
}
span.star2c {
color:#ff455c;
}
span.star3c {
color:#00b0f9;
}
span.star4c {
color:#a411b9;
}
span.orange{
color: #ff8d45;
}
span.green {
color: #82c300;
}
span.gray{
color: #666666;
}
span.white {
color:#FFFFFF;
}
html {
margin:0;
padding:0;
}
body {
color:#333333;
margin:0;
padding:0;
}
div.title {
margin:0 7px 0 7px;
padding:7px 5px 7px 5px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.description {
margin:0 7px 0 7px;
padding:7px 5px 7px 5px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.hr {
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.hr-tab {
border-style:solid;
border-width:0 0 1px 0;
border-color:#82c300;
}
span.timestamp {
color:#999999;
}
a.timestamp {
color:#999999;
}
a.violation {
color:#333333;
}
div.box {
padding:7px;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
background-color:#FFFFFF;
}
div.more {
margin:7px 0 0 0;
}
div.pager {
padding:5px;
}
div.num {
margin:5px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
background-color:#FFFFFF;
}
span.new {
color: #cc0000;
}
span.price {
color: #92cb22;
}
span.date {
color: #cccccc;
}
table.detail{
margin:0 7px 0 7px;
}
table.fullsize {
margin:0;
}
div.item-term {
padding:3px 0 3px 5px;
}
div.item-value {
padding:3px 5px 3px 0;
}
div.item-text {
padding:3px 5px 3px 15px;
}
div.item-tcom {
padding: 3px 5px 0 5px;
}
div.item-com {
padding: 0 5px 0 5px;
}
div.td-body {
padding:3px 5px 3px 5px;
}
div.comlist {
background-color:#FFFFFF;
}
div.comment-text {
padding:3px 5px 5px 15px;
}
div.comment-memo {
padding:0 0 0 15px;
}
div.staff {
color:#FFFFFF;
background-color:#1a1a1a;
}
a.staffnam {
color:#FFFFFF;
}
div.notice {
width:226px;
margin:7px 7px 3px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.colorstar {
margin:7px 0 7px 0;
}
div.code-box{
width:226px;
margin:7px 0 0 0;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.code-data{
margin:7px 0 7px 0;
}
div.mail-reply {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.lett_txt {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0 2px;
border-color:#92d300;
background-color:#ffffff;
}
div.lett_to {
margin: 0 11px 0 11px;
background-color:#92d300;
color:#ffffff;
padding: 1px 0 6px 5px;
}
div.pad0{
padding: 0 0 0 0;
}
div.pad5t{
padding: 5px 0 0 0;
}
div.pad5b{
padding: 0 0 5px 0;
}
div.pad1b{
padding: 0 0 1px 0;
}
div.pad2b{
padding: 0 0 2px 0;
}
div.pad2{
padding: 2px 2px 2px 2px;
}
div.pad5{
padding: 5px 5px 5px 5px;
}
div.pad0-5{
padding: 0 5px 0 5px;
}
td.pad2{
padding: 2px 2px 2px 2px;
}
div.pad3-5{
padding: 3px 5px 3px 5px;
}
div.lett_from{
padding: 3px 11px 3px 0;
}
div.pad11{
padding: 11px 11px 11px 11px;
}
div.pad5-11{
padding: 5px 11px 5px 11px;
}
div.pad10b{
padding: 0 0 10px 0;
}
div.pad20b{
padding: 0 0 20px 0;
}
div.mar0-7{
margin: 0px 7px 0px 7px;
}
div.mar0-11{
margin: 0px 11px 0px 11px;
}
div.mar0{
margin: 0px 0px 0px 0px;
}
div.mar5 {
margin: 5px 5px 5px 5px;
}
div.yaritori{
margin: 0 11px 0 11px;
background-color:#ffffff;
border-style:solid;
border-width:0 2px 0 2px;
border-color:#92d300;
}
div.gr-tit{
margin: 0 11px 0 11px;
background-color:#92d300;
color:#ffffff;
}
div.kadomaru{
margin: 0 11px 0 11px;
}
div.lett-note{
margin: 5px 11px 5px 11px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92d300;
background-color:#ffffff;
}
div.pk{
background-color:#ff3ab7;
}
div.blue{
background-color:#3acfff;
}
div.gray{
background-color:#cccccc;
}
div.gray_box{
padding:5px;
border-style:solid;
border-width:1px;
border-color:#cccccc;
background-color:#ffffff;
}
div.vio-intro {
width:240px;
padding:7px 0 7px 7px;
}
div.vio-memo {
padding:0 0 10px 15px;
}
div.vio-text {
padding:0 0 10px 15px;
}
div.vio-note {
color:#999999;
padding:0 0 10px 15px;
}
div.vio-submit {
padding:10px 0 10px 0;
}
div.info-head {
padding:5px 7px 5px 7px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
background-color:#eeeeee;
}
div.noborder{
border-style:solid;
border-color:#CCCCCC;
border-width:0 0 0 0;
}
div.info-body {
padding:7px;
}
div.info-msg {
margin:0 0 7px 0;
}
div.news-head {
padding:5px 7px 5px 7px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
background-color:#eeeeee;
}
div.news-body {
padding:7px;
}
div.news-h1 {
margin:7px 7px 0 7px;
padding:7px;
border-style:solid;
border-width:3px;
border-color:#91C900;
}
div.news-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#91C900;
}
img.news-memo {
margin:0 10px 5px 0;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
}
div.info-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#91C900;
}
div.num-box{
background-color:#cccccc;
margin:0 30px;
}
td.tit-star{
background-color:#e9f8c3;
}
img.help{
border-style:solid;
border-width:2px;
border-color:#ffffff;
}
div.list{
padding:0 0 5px 0;
}
div.star-det{
padding:5px 0 5px 0;
}
img.sc{
border-style:solid;
border-width:1px;
border-color:#cccccc;
}
td.border {
background-color: #82c300;
}
div.border {
border-style: solid;
border-width: 0 0 5px 0;
border-color: #82c300;
}
div.border-c {
border-style: solid;
border-width: 0 0 2px 0;
border-color: #82c300;
}
td.space {
background-color: #82c300;
color: #82c300;
}
td.tabon {
color: #82c300;
}
td.taboff {
background-color: #82c300;
color: #ffffff;
}
td.tabpager {
background-color: #82c300;
color: #ffffff;
}
div.on {
padding: 1px 6px 3px 6px;
}
span.border {
color: #b2db59;
}
span.border-gray {
color: #cccccc;
}
a.taboff {
color: #ffffff;
}
div.comme {
padding: 3px 3px 3px 12px;
color: #b2db59;
}
span.text {
color: #333333;
}
td.gray {
background-color: #f2f2f2;
}
div.message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.error-message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#F5C8A7;
background-color:#FFEED8;
color:#9B2E2E;
}
div.ok-message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#C5DABA;
background-color:#E3F9DB;
color:#517B29;
}
div.guide-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#66ccff;
}
div.guide-h3 {
padding:3px 3px 3px 3px;
background-color:#66ccff;
color: #ffffff;
}
div.select-t {
color: #66ccff;
padding: 0 0 3px 0;
}
div.select-b {
padding:5px 3px 5px 3px;
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #99ccff;
background-color: #ffffff;
}
span.border-s {
color: #99ccff;
}
span.color {
color: #ffffff;
}
div.credit {
padding:5px 7px 5px 7px;
background-color:#eeeeee;
}
div.rule-tit {
background-color: #82c300;
color: #ffffff;
padding: 3px 3px 3px 3px;
}
div.rule-ind {
margin: 0 0 0 10px;
}
div.hr-prof {
border-style:solid;
border-width:0 0 3px 0;
border-color:transparent;
}
table.send {
margin: 5px 0 0 0 ;
}
div.icon {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #cccccc;
}
td.username {
height:20px;
}
td.mystatus {
}
div.prev {
padding: 1px 0 5px 0;
}
div.next {
padding: 5px 0 1px 0;
}
div.prof-det{
padding: 0 5px 0 5px;
}
div.catch {
margin: 5px 5px 5px 5px;
padding: 5px 5px 5px 5px;
border-style: solid;
border-width: 2px 2px 2px 2px;
border-color: #ffffff;
}
a.still {
color: #ffffff;
}
div.head {
margin: 0px 0 0 0;
}
div.send {
margin: 0px 10px 3px 11px;
}
div.link {
margin: 0px 10px 0 11px;
}
img.button {
margin: 5px 0 0 0;
}
td.theme {
margin: 0 0 0 3px;
}
div.trial-h1 {
margin: 5px 0 5px 0;
padding:3px;
border-style:solid;
border-width:3px;
border-color:#91C900;
}
div.mar-b10{
margin:0 0 10px 0;
}
div.cam1{
margin:7px 7px 0 7px;
padding:7px;
background-color: #ff2828;
color: #ffffff;
}
div.cam2{
margin:0 7px 0 7px;
padding:7px;
border-style:solid;
border-width: 2px 2px 2px 2px;
border-color: #ff2828;
}
table.xmas2009{
margin: 0 10px 0 11px;
background-color: #c70606;
color: #ffffff;
}
div.yellow {
margin: 0 11px 0 11px;
background-color:#ffe800;
}
div.sttr_mes {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_gacha {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#ea475c;
background-color:#ffffff;
}
div.compsp {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#ffe800;
background-color:#fff9b7;
}
div.sttr_war {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#F5C8A7;
background-color:#FFEED8;
color:#9B2E2E;
}
div.sttr_price {
background-color:#FF9933;
color: #ffffff;
}
div.sttr_item {
width:226px;
margin:0px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_theme {
width:226px;
margin:0px 7px 3px 7px;
padding:5px;
border-style:solid;
border-width:0px 1px 1px 1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_back {
width:226px;
margin:0px 7px 0px 7px;
}
div.gift_l {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0px 2px;
border-color:#ff5252;
}
div.gift_h {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0px 2px;
border-color:#ffd616;
}
img.shop_icon {
margin:0 1px 1px 0;
}
div.help_guide {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#ffac3e;
}
div.guide-head {
color: #ffffff;
background-color: #5abcea;
}
div.guide-title {
color: #ffffff;
background-color: #5abcea;
}
div.guide-main {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#5abcea;
}
div.green-title {
color: #ffffff;
background-color: #91C900;
}
div.green-main {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#91C900;
}
table.method{
border-style:solid;
border-width:1px;
border-color:#cccccc;
}
div.search {
background-color: #eeeeee;
border-style: solid;
border-width: 1px;
border-color:#cccccc;
}
td.keyboard {
background-color: #eeeeee;
}
table.key-tab{
margin: 0 6px 0 6px;
}
div.keyout {
margin: 0 7px 0 7px;
border-style: solid;
border-width: 0 1px 1px 1px;
border-color:#cccccc;
}
div.on-ja {
border-style: solid;
border-width: 1px 1px 0 1px;
border-color:#cccccc;
}
div.off-en {
border-style: solid;
border-width: 1px 1px 0 0;
border-color:#cccccc;
}
div.off-aa {
border-style: solid;
border-width: 1px 1px 0 0;
border-color:#cccccc;
}
table.camp-detail {
border-style: solid;
border-width: 1px;
border-color:#cccccc;
}
table.center {
margin: 0 15px 0 16px;
}
div.codeborder {
border-style: solid;
border-width:0 1px 1px 1px;
border-color:#82c300;
}
div.codeprev {
border-style: solid;
border-width:1px;
border-color:#82c300;
}
td.code {
background-color:#ffffff;
}
table.codetab {
border-style: solid;
border-width: 1px 1px 0 1px;
border-color:#82c300;
}
div.codetab {
border-style: solid;
border-width: 0 1px 1px 1px;
border-color:#82c300;
background-color:#ffffff;
}
div.c_tab2 {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color:#82c300;
background-color:#ffffff;
}
div.topic {
border-style:solid;
border-width:1px;
border-color:#fff273;
background-color:#fff8b2;
}
div.icon-green {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #82c300;
}
div.p-block {
border-style:solid;
border-width:1px;
border-color:#000000;
color:#000000;
background-color:#ffffff;
}
div.clstar-help{
background-color:#ffffff;
color:#82c300;
}
div.ugo4th{
border-style:solid;
border-width:1px;
border-color:#c4260f;
background-color:#d52515;
}
+808
View File
@@ -0,0 +1,808 @@
@charset "UTF-8";
span.url{
text-decoration:underline;
}
span.comptxt {
color:#ff8a00;
}
span.star0 {
color:#fdc556;
}
span.star1 {
color:#138200;
}
span.star2 {
color:#ff455c;
}
span.star3 {
color:#00b0f9;
}
span.star4 {
color:#a411b9;
}
span.star0c {
color:#fdc556;
}
span.star1c {
color:#138200;
}
span.star2c {
color:#ff455c;
}
span.star3c {
color:#00b0f9;
}
span.star4c {
color:#a411b9;
}
span.orange{
color: #ff8d45;
}
span.green {
color: #82c300;
}
span.gray{
color: #666666;
}
span.white {
color:#FFFFFF;
}
html {
margin:0;
padding:0;
}
body {
color:#333333;
margin:0;
padding:0;
}
div.title {
margin:0 7px 0 7px;
padding:7px 5px 7px 5px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.description {
margin:0 7px 0 7px;
padding:7px 5px 7px 5px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.hr {
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
}
div.hr-tab {
border-style:solid;
border-width:0 0 1px 0;
border-color:#82c300;
}
span.timestamp {
color:#999999;
}
a.timestamp {
color:#999999;
}
a.violation {
color:#333333;
}
div.box {
padding:7px;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
background-color:#FFFFFF;
}
div.more {
margin:7px 0 0 0;
}
div.pager {
padding:5px;
}
div.num {
margin:5px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
background-color:#FFFFFF;
}
span.new {
color: #cc0000;
}
span.price {
color: #92cb22;
}
span.date {
color: #cccccc;
}
table.detail{
margin:0 7px 0 7px;
}
table.fullsize {
margin:0;
}
div.item-term {
padding:3px 0 3px 5px;
}
div.item-value {
padding:3px 5px 3px 0;
}
div.item-text {
padding:3px 5px 3px 15px;
}
div.item-tcom {
padding: 3px 5px 0 5px;
}
div.item-com {
padding: 0 5px 0 5px;
}
div.td-body {
padding:3px 5px 3px 5px;
}
div.comlist {
background-color:#FFFFFF;
}
div.comment-text {
padding:3px 5px 5px 15px;
}
div.comment-memo {
padding:0 0 0 15px;
}
div.staff {
color:#FFFFFF;
background-color:#1a1a1a;
}
a.staffnam {
color:#FFFFFF;
}
div.notice {
width:226px;
margin:7px 7px 3px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.colorstar {
margin:7px 0 7px 0;
}
div.code-box{
width:226px;
margin:7px 0 0 0;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.code-data{
margin:7px 0 7px 0;
}
div.mail-reply {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.lett_txt {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0 2px;
border-color:#92d300;
background-color:#ffffff;
}
div.lett_to {
margin: 0 11px 0 11px;
background-color:#92d300;
color:#ffffff;
padding: 1px 0 6px 5px;
}
div.pad0{
padding: 0 0 0 0;
}
div.pad5t{
padding: 5px 0 0 0;
}
div.pad5b{
padding: 0 0 5px 0;
}
div.pad1b{
padding: 0 0 1px 0;
}
div.pad2b{
padding: 0 0 2px 0;
}
div.pad2{
padding: 2px 2px 2px 2px;
}
div.pad5{
padding: 5px 5px 5px 5px;
}
div.pad0-5{
padding: 0 5px 0 5px;
}
td.pad2{
padding: 2px 2px 2px 2px;
}
div.pad3-5{
padding: 3px 5px 3px 5px;
}
div.lett_from{
padding: 3px 11px 3px 0;
}
div.pad11{
padding: 11px 11px 11px 11px;
}
div.pad5-11{
padding: 5px 11px 5px 11px;
}
div.pad10b{
padding: 0 0 10px 0;
}
div.pad20b{
padding: 0 0 20px 0;
}
div.mar0-7{
margin: 0px 7px 0px 7px;
}
div.mar0-11{
margin: 0px 11px 0px 11px;
}
div.mar0{
margin: 0px 0px 0px 0px;
}
div.mar5 {
margin: 5px 5px 5px 5px;
}
div.yaritori{
margin: 0 11px 0 11px;
background-color:#ffffff;
border-style:solid;
border-width:0 2px 0 2px;
border-color:#92d300;
}
div.gr-tit{
margin: 0 11px 0 11px;
background-color:#92d300;
color:#ffffff;
}
div.kadomaru{
margin: 0 11px 0 11px;
}
div.lett-note{
margin: 5px 11px 5px 11px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92d300;
background-color:#ffffff;
}
div.pk{
background-color:#ff3ab7;
}
div.blue{
background-color:#3acfff;
}
div.gray{
background-color:#cccccc;
}
div.gray_box{
padding:5px;
border-style:solid;
border-width:1px;
border-color:#cccccc;
background-color:#ffffff;
}
div.vio-intro {
width:240px;
padding:7px 0 7px 7px;
}
div.vio-memo {
padding:0 0 10px 15px;
}
div.vio-text {
padding:0 0 10px 15px;
}
div.vio-note {
color:#999999;
padding:0 0 10px 15px;
}
div.vio-submit {
padding:10px 0 10px 0;
}
div.info-head {
padding:5px 7px 5px 7px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
background-color:#eeeeee;
}
div.noborder{
border-style:solid;
border-color:#CCCCCC;
border-width:0 0 0 0;
}
div.info-body {
padding:7px;
}
div.info-msg {
margin:0 0 7px 0;
}
div.news-head {
padding:5px 7px 5px 7px;
border-style:solid;
border-width:0 0 1px 0;
border-color:#CCCCCC;
background-color:#eeeeee;
}
div.news-body {
padding:7px;
}
div.news-h1 {
margin:7px 7px 0 7px;
padding:7px;
border-style:solid;
border-width:3px;
border-color:#91C900;
}
div.news-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#91C900;
}
img.news-memo {
margin:0 10px 5px 0;
border-style:solid;
border-width:1px;
border-color:#CCCCCC;
}
div.info-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#91C900;
}
div.num-box{
background-color:#cccccc;
margin:0 30px;
}
td.tit-star{
background-color:#e9f8c3;
}
img.help{
border-style:solid;
border-width:2px;
border-color:#ffffff;
}
div.list{
padding:0 0 5px 0;
}
div.star-det{
padding:5px 0 5px 0;
}
img.sc{
border-style:solid;
border-width:1px;
border-color:#cccccc;
}
td.border {
background-color: #82c300;
}
div.border {
border-style: solid;
border-width: 0 0 5px 0;
border-color: #82c300;
}
div.border-c {
border-style: solid;
border-width: 0 0 2px 0;
border-color: #82c300;
}
td.space {
background-color: #82c300;
color: #82c300;
}
td.tabon {
color: #82c300;
}
td.taboff {
background-color: #82c300;
color: #ffffff;
}
td.tabpager {
background-color: #82c300;
color: #ffffff;
}
div.on {
padding: 1px 6px 3px 6px;
}
span.border {
color: #b2db59;
}
span.border-gray {
color: #cccccc;
}
a.taboff {
color: #ffffff;
}
div.comme {
padding: 3px 3px 3px 12px;
color: #b2db59;
}
span.text {
color: #333333;
}
td.gray {
background-color: #f2f2f2;
}
div.message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#BCCDED;
background-color:#E5EEFE;
}
div.error-message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#F5C8A7;
background-color:#FFEED8;
color:#9B2E2E;
}
div.ok-message {
width:226px;
margin:7px 7px 2px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#C5DABA;
background-color:#E3F9DB;
color:#517B29;
}
div.guide-h2 {
padding:0 0 3px 7px;
border-style:solid;
border-width:0 0 1px 4px;
border-color:#66ccff;
}
div.guide-h3 {
padding:3px 3px 3px 3px;
background-color:#66ccff;
color: #ffffff;
}
div.select-t {
color: #66ccff;
padding: 0 0 3px 0;
}
div.select-b {
padding:5px 3px 5px 3px;
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #99ccff;
background-color: #ffffff;
}
span.border-s {
color: #99ccff;
}
span.color {
color: #ffffff;
}
div.credit {
padding:5px 7px 5px 7px;
background-color:#eeeeee;
}
div.rule-tit {
background-color: #82c300;
color: #ffffff;
padding: 3px 3px 3px 3px;
}
div.rule-ind {
margin: 0 0 0 10px;
}
div.hr-prof {
border-style:solid;
border-width:0 0 3px 0;
border-color:transparent;
}
table.send {
margin: 5px 0 0 0 ;
}
div.icon {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #cccccc;
}
td.username {
height:20px;
}
td.mystatus {
}
div.prev {
padding: 1px 0 5px 0;
}
div.next {
padding: 5px 0 1px 0;
}
div.prof-det{
padding: 0 5px 0 5px;
}
div.catch {
margin: 5px 5px 5px 5px;
padding: 5px 5px 5px 5px;
border-style: solid;
border-width: 2px 2px 2px 2px;
border-color: #ffffff;
}
a.still {
color: #ffffff;
}
div.head {
margin: 0px 0 0 0;
}
div.send {
margin: 0px 10px 3px 11px;
}
div.link {
margin: 0px 10px 0 11px;
}
img.button {
margin: 5px 0 0 0;
}
td.theme {
margin: 0 0 0 3px;
}
div.trial-h1 {
margin: 5px 0 5px 0;
padding:3px;
border-style:solid;
border-width:3px;
border-color:#91C900;
}
div.mar-b10{
margin:0 0 10px 0;
}
div.cam1{
margin:7px 7px 0 7px;
padding:7px;
background-color: #ff2828;
color: #ffffff;
}
div.cam2{
margin:0 7px 0 7px;
padding:7px;
border-style:solid;
border-width: 2px 2px 2px 2px;
border-color: #ff2828;
}
table.xmas2009{
margin: 0 10px 0 11px;
background-color: #c70606;
color: #ffffff;
}
div.yellow {
margin: 0 11px 0 11px;
background-color:#ffe800;
}
div.sttr_mes {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_gacha {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#ea475c;
background-color:#ffffff;
}
div.compsp {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#ffe800;
background-color:#fff9b7;
}
div.sttr_war {
width:226px;
margin:7px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#F5C8A7;
background-color:#FFEED8;
color:#9B2E2E;
}
div.sttr_price {
background-color:#FF9933;
color: #ffffff;
}
div.sttr_item {
width:226px;
margin:0px 7px 0px 7px;
padding:5px;
border-style:solid;
border-width:1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_theme {
width:226px;
margin:0px 7px 3px 7px;
padding:5px;
border-style:solid;
border-width:0px 1px 1px 1px;
border-color:#92cb22;
background-color:#ffffff;
}
div.sttr_back {
width:226px;
margin:0px 7px 0px 7px;
}
div.gift_l {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0px 2px;
border-color:#ff5252;
}
div.gift_h {
margin: 0 11px 0 11px;
border-style:solid;
border-width:0 2px 0px 2px;
border-color:#ffd616;
}
img.shop_icon {
margin:0 1px 1px 0;
}
div.help_guide {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#ffac3e;
}
div.guide-head {
color: #ffffff;
background-color: #5abcea;
}
div.guide-title {
color: #ffffff;
background-color: #5abcea;
}
div.guide-main {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#5abcea;
}
div.green-title {
color: #ffffff;
background-color: #91C900;
}
div.green-main {
border-style:solid;
border-width:0 1px 1px 1px;
border-color:#91C900;
}
table.method{
border-style:solid;
border-width:1px;
border-color:#cccccc;
}
div.search {
background-color: #eeeeee;
border-style: solid;
border-width: 1px;
border-color:#cccccc;
}
td.keyboard {
background-color: #eeeeee;
}
table.key-tab{
margin: 0 6px 0 6px;
}
div.keyout {
margin: 0 7px 0 7px;
border-style: solid;
border-width: 0 1px 1px 1px;
border-color:#cccccc;
}
div.on-ja {
border-style: solid;
border-width: 1px 1px 0 1px;
border-color:#cccccc;
}
div.off-en {
border-style: solid;
border-width: 1px 1px 0 0;
border-color:#cccccc;
}
div.off-aa {
border-style: solid;
border-width: 1px 1px 0 0;
border-color:#cccccc;
}
table.camp-detail {
border-style: solid;
border-width: 1px;
border-color:#cccccc;
}
table.center {
margin: 0 15px 0 16px;
}
div.codeborder {
border-style: solid;
border-width:0 1px 1px 1px;
border-color:#82c300;
}
div.codeprev {
border-style: solid;
border-width:1px;
border-color:#82c300;
}
td.code {
background-color:#ffffff;
}
table.codetab {
border-style: solid;
border-width: 1px 1px 0 1px;
border-color:#82c300;
}
div.codetab {
border-style: solid;
border-width: 0 1px 1px 1px;
border-color:#82c300;
background-color:#ffffff;
}
div.c_tab2 {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color:#82c300;
background-color:#ffffff;
}
div.topic {
border-style:solid;
border-width:1px;
border-color:#fff273;
background-color:#fff8b2;
}
div.icon-green {
border-style: solid;
border-width: 1px 1px 1px 1px;
border-color: #82c300;
}
div.p-block {
border-style:solid;
border-width:1px;
border-color:#000000;
color:#000000;
background-color:#ffffff;
}
div.clstar-help{
background-color:#ffffff;
color:#82c300;
}
div.ugo4th{
border-style:solid;
border-width:1px;
border-color:#c4260f;
background-color:#d52515;
}
+1
View File
@@ -0,0 +1 @@
����������������������������������������������������������������ςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σςς�Σ�Σςς�Σ�Σ�Σ�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�ΣΟ‚�Σςςςς�ΣΟ‚�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�ΣΟ‚�Σςςςς�ΣΟ‚�Σ�Σ�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςςςςςςςς�Σ�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σςς�Σ�Σ�Σ�Σ�Σ�Σςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σςςς�Σ�Σ�Σ�Σ�Σ�Σςςς�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σςςςς�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςς�Σ�Σςςςς�Σ�Σ�Σ�Σςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σςςςςςςςςςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςςςςςςςςςςςςς�Σςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςςςς�Σςςςςςςςςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςςςςςςς�Σςςςςςςςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σςςςςςςςςςςςςςςς�Σςςςςςς�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςς�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σ�Σςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςςς����������������������������������������������������������������
@@ -0,0 +1 @@
���������������サ纎ロワ������������������������ン�モォョ完完完完完諌�5ササ゚������������������ン�ソョ完完完完完完完完完完完寄椢ロ���������������xマョ完完完完完諌儒�潁漬式完完完完岐「ワ������������5キョ完完完�5」垤�ヌ�ヒ�ヒ�ヒ�ヒ�ヒシソxッム聴完完官噛゚����������ッョ完完岐埆サ�ヒ�ヒ�ヒ�ヒ�ヒWォョ岸テ�ヒ�ヒ�ヒyウョ完完完噛゚��������Vテョ完完碍ァ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォョ寄繭キ�ヒ�ヒ�ヒシソミ聴完完款������壙ョ完完蓋ォ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォョ完完官�6ァンテ�ヒンテミ聴完貫������ミ粍完�4」�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォッ式完完完官孜ウ�ヒワソョ完完楽モ����燮ョ完諌釣ヌ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ�シソ5」ョ完完官爵テ�ヒ垤ョ完観�����ョ完噛キ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヌWォョ完棺サ�ヒ�ヒ�ョ完噛ラ��ワ�完諌�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ5」ョ岸テ�ヒ�ヒ垤ョ完�ォ��Wテョ完�5」�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヌミ�6ァ�ヒ�ヒ�ヒ�ヌョ完完��ウョ完噛キ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒンテ��ヒ�ヒ�ヒ�ヒ�ヒ�ョ完款��ョ完款ソ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ4洌完鎌ラ�ム坩完岸テ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ5」ョ完岳マ�ム坩完岸テ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ5」ョ完岳マ��ョ完棺サ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ洌完噛ラ�5サョ完岳ッ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ョ完含�yモョ完�5」�ヒ�ヒ�ヒ�ヒ�ヒンテxッ漑�6ァササWォ��ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒンテョ完観��ン�完官岸テ�ヒ�ヒ�ヒ6ァョ完完完完完諌梯�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒWォョ完�5キ���4キョ完碍ァ�ヒ�ヒシソョ完完完完完完完岐�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒンテッ完完棺���サ羂完完款ソ�ヒササョ完完完完完完完岐�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒVァョ完岐ヲ�����ウョ完諌釣テ�ヒ�ョ完完完完完官湿ソ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ匍ョ完完棺�����シ�完完�巫テ�ヒyウWォ��6ァyウ�ヌ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒササョ完完�4ウ�������壤ョ完完寄吊ソ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒxッョ完完寄���������xマョ完完完学ォ�ヌ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒ�ヒササ洌完完諌吊����������壅ョ完完完諌�5」垤ンデテ�ヌ゙テササxッ岼完完完岐ヲ�������������シ�ォョ完完完完完完完完完完完完完完蓋ヌ����������������サ゚ッョ完完完完完完完完完官桟ソワ�������������������ワ�マッム墟佃漬梯「5ササ゚�������������������������������������������
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
RQBuAGcAbABpAHMAaAA= en
1 RQBuAGcAbABpAHMAaAA= en
@@ -0,0 +1,94 @@
from twisted.web import resource
from twisted.internet import reactor
import time
from hatena import Database, Log, NotFound
from Hatenatools import UGO
#makes a flipnote list of the most seen ones among the recently uploaded
class PyResource(resource.Resource):
isLeaf = True
def __init__(self):
resource.Resource.__init__(self)
self.pages = []#10 first pages
self.newestview = None
self.Update()
def render(self, request):
page = int(request.args["page"][0]) if "page" in request.args else 1
if len(self.pages) >= page:
path = "/".join(request.path.split("/")[3:])
Log(request, "%s page %i" % (path, page))
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return self.pages[page-1]
else:
return NotFound.render(request)
#===
def Update(self):#called every 15 minutes
reactor.callLater(60*10, self.Update)
if self.newestview <> Database.Views:
self.newestview = Database.Views
reactor.callInThread(self.UpdateThreaded, Database.Newest)
def UpdateThreaded(self, flipnotes):#run in an another thread
#sort the flipnotes by viewcount, affected by amount of stars
def sort((i, (ID, flip))):
views, stars = Database.GetFlipnote(ID, flip)[1:3]
return int(views)*60 + int(stars)/4 - i
flipnotes = map(lambda x: x[1], sorted(enumerate(flipnotes), key=sort)[::-1])[:500]
#create pages:
pages = []
pagecount = (len(flipnotes)-1)/50 + 1
if pagecount > 10: pagecount = 10#temp?
flipcount = len(flipnotes) if len(flipnotes) < 500 else 500
for i in xrange(pagecount):
pages.append(self.MakePage(flipnotes[i*50:i*50+50], i+1, i<pagecount-1, flipcount))
if self.pages:#not on startup
print time.strftime("[%H:%M:%S] Updated hotmovies.ugo")
self.pages = pages
def MakePage(self, flipnotes, page, next, count):
ugo = UGO()
ugo.Loaded = True
ugo.Items = []
#meta
ugo.Items.append(("layout", (2, 1)))
ugo.Items.append(("topscreen text", ["Popular Flipnotes", "Flipnotes", str(count), "", "The most popular new Flipnotes."], 0))
#categories
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/hotmovies.uls", "Most Popular", True))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/likedmovies.uls", "Most Liked", False))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/recommended.uls", "Recommended", False))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/newmovies.uls", "New Flipnotes", False))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/following.uls", "New Favourites", False))
#the "post flipnote" button
ugo.Items.append(("unknown", ("3", "http://flipnote.hatena.com/ds/v2-xx/help/post_howto.htm", "UABvAHMAdAAgAEYAbABpAHAAbgBvAHQAZQA=")))
#previous page
if page > 1:
ugo.Items.append(("button", 115, "Previous", "http://flipnote.hatena.com/ds/v2-xx/frontpage/hotmovies.uls?page=%i" % (page-1), ("", ""), None))
#Flipnotes
for creatorid, filename in flipnotes:#[i*50 : i*50+50]:
stars = str(Database.GetFlipnote(creatorid, filename)[2])
f = open(Database.FlipnotePath(creatorid, filename+".ppm"), "rb")
ugo.Items.append(("button", 3, "", "http://flipnote.hatena.com/ds/v2-xx/movie/%s/%s.ppm" % (creatorid, filename), (stars, "765", "573", "0"), ("bleh", f.read(0x6a0))))
f.close()
#next page
if next:
ugo.Items.append(("button", 115, "Next", "http://flipnote.hatena.com/ds/v2-xx/frontpage/hotmovies.uls?page=%i" % (page+1), ("", ""), None))
return ugo.Pack()
@@ -0,0 +1,94 @@
from twisted.web import resource
from twisted.internet import reactor
import time
from hatena import Database, Log, NotFound
from Hatenatools import UGO
#makes a flipnote list of the most seen ones among the recently uploaded
class PyResource(resource.Resource):
isLeaf = True
def __init__(self):
resource.Resource.__init__(self)
self.pages = []#10 first pages
self.newestview = None
self.Update()
def render(self, request):
page = int(request.args["page"][0]) if "page" in request.args else 1
if len(self.pages) >= page:
path = "/".join(request.path.split("/")[3:])
Log(request, "%s page %i" % (path, page))
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return self.pages[page-1]
else:
return NotFound.render(request)
#===
def Update(self):#called every 15 minutes
reactor.callLater(60*10, self.Update)
if self.newestview <> Database.Views:
self.newestview = Database.Views
reactor.callInThread(self.UpdateThreaded, Database.Newest)
def UpdateThreaded(self, flipnotes):#run in an another thread
#sort the flipnotes by viewcount, affected by amount of stars
def sort((i, (ID, flip))):
views, stars = Database.GetFlipnote(ID, flip)[1:3]
return int(stars)*110 + int(views)/10 - i
flipnotes = map(lambda x: x[1], sorted(enumerate(flipnotes), key=sort)[::-1])[:500]
#create pages:
pages = []
pagecount = (len(flipnotes)-1)/50 + 1
if pagecount > 10: pagecount = 10#temp?
flipcount = len(flipnotes) if len(flipnotes) < 500 else 500
for i in xrange(pagecount):
pages.append(self.MakePage(flipnotes[i*50:i*50+50], i+1, i<pagecount-1, flipcount))
if self.pages:#not on startup
print time.strftime("[%H:%M:%S] Updated hotmovies.ugo")
self.pages = pages
def MakePage(self, flipnotes, page, next, count):
ugo = UGO()
ugo.Loaded = True
ugo.Items = []
#meta
ugo.Items.append(("layout", (2, 1)))
ugo.Items.append(("topscreen text", ["Liked Flipnotes", "Flipnotes", str(count), "", "The most liked new Flipnotes."], 0))
#categories
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/hotmovies.uls", "Most Popular", False))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/likedmovies.uls", "Most Liked", True))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/recommended.uls", "Recommended", False))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/newmovies.uls", "New Flipnotes", False))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/following.uls", "New Favourites", False))
#the "post flipnote" button
ugo.Items.append(("unknown", ("3", "http://flipnote.hatena.com/ds/v2-xx/help/post_howto.htm", "UABvAHMAdAAgAEYAbABpAHAAbgBvAHQAZQA=")))
#previous page
if page > 1:
ugo.Items.append(("button", 115, "Previous", "http://flipnote.hatena.com/ds/v2-xx/frontpage/likedmovies.uls?page=%i" % (page-1), ("", ""), None))
#Flipnotes
for creatorid, filename in flipnotes:#[i*50 : i*50+50]:
stars = str(Database.GetFlipnote(creatorid, filename)[2])
f = open(Database.FlipnotePath(creatorid, filename+".ppm"), "rb")
ugo.Items.append(("button", 3, "", "http://flipnote.hatena.com/ds/v2-xx/movie/%s/%s.ppm" % (creatorid, filename), (stars, "765", "573", "0"), ("bleh", f.read(0x6a0))))
f.close()
#next page
if next:
ugo.Items.append(("button", 115, "Next", "http://flipnote.hatena.com/ds/v2-xx/frontpage/likedmovies.uls?page=%i" % (page+1), ("", ""), None))
return ugo.Pack()
@@ -0,0 +1,91 @@
from twisted.web import resource
from twisted.internet import reactor
import time
from hatena import Database, Log, NotFound
from Hatenatools import UGO
#makes a flipnote list of the newest flipnotes
class PyResource(resource.Resource):
isLeaf = True
def __init__(self):
resource.Resource.__init__(self)
self.pages = []#10 first pages
self.newestflip = None
self.Update()
def render(self, request):
page = int(request.args["page"][0]) if "page" in request.args else 1
if len(self.pages) >= page:
path = "/".join(request.path.split("/")[3:])
Log(request, "%s page %i" % (path, page))
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return self.pages[page-1]
#elif (page-1)*50 < len(Database.Newest):
# pass
else:
return NotFound.render(request)
#===
def Update(self):#called every minute
reactor.callLater(60, self.Update)
if self.newestflip <> Database.Newest[0]:
self.newestflip = Database.Newest[0]
reactor.callInThread(self.UpdateThreaded, Database.Newest)
def UpdateThreaded(self, flipnotes):#run in an another thread
#create pages:
pages = []
pagecount = (len(flipnotes)-1)/50 + 1
if pagecount > 10: pagecount = 10#temp?
flipcount = len(flipnotes)
for i in xrange(min((pagecount, 10))):
pages.append(self.MakePage(flipnotes[i*50:i*50+50], i+1, i<pagecount-1, flipcount))
if self.pages:#not on startup
print time.strftime("[%H:%M:%S] Updated newmovies.ugo")
self.pages = pages
def MakePage(self, flipnotes, page, next, count):
ugo = UGO()
ugo.Loaded = True
ugo.Items = []
#meta
ugo.Items.append(("layout", (2, 1)))
ugo.Items.append(("topscreen text", ["New Flipnotes", "Flipnotes", str(count), "", "The newest Flipnotes submitted."], 0))
#categories
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/hotmovies.uls", "Most Popular", False))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/likedmovies.uls", "Most Liked", False))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/recommended.uls", "Recommended", False))
ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/newmovies.uls", "New Flipnotes", True))
#ugo.Items.append(("category", "http://flipnote.hatena.com/ds/v2-xx/frontpage/following.uls", "New Favourites", False))
#the "post flipnote" button
ugo.Items.append(("unknown", ("3", "http://flipnote.hatena.com/ds/v2-xx/help/post_howto.htm", "UABvAHMAdAAgAEYAbABpAHAAbgBvAHQAZQA=")))
#previous page
if page > 1:
ugo.Items.append(("button", 115, "Previous", "http://flipnote.hatena.com/ds/v2-xx/frontpage/newmovies.uls?page=%i" % (page-1), ("", ""), None))
#Flipnotes
for creatorid, filename in flipnotes:#[i*50 : i*50+50]:
stars = str(Database.GetFlipnote(creatorid, filename)[2])
f = open(Database.FlipnotePath(creatorid, filename+".ppm"), "rb")
ugo.Items.append(("button", 3, "", "http://flipnote.hatena.com/ds/v2-xx/movie/%s/%s.ppm" % (creatorid, filename), (stars, "765", "573", "0"), ("bleh", f.read(0x6a0))))
f.close()
#next page
if next:
ugo.Items.append(("button", 115, "Next", "http://flipnote.hatena.com/ds/v2-xx/frontpage/newmovies.uls?page=%i" % (page+1), ("", ""), None))
return ugo.Pack()
+1
View File
@@ -0,0 +1 @@
<html>␍ <head>␍ <title>Post Flipnote</title>␍ <meta name="uppertitle" content="Post Flipnote">␍ <meta name="postchannelbutton" content="http://flipnote.hatena.com/ds/v2-xx/post/flipnote.post">␍ <link rel="stylesheet" type="text/css" href="http://flipnote.hatena.com/css/ds/common.css">␍ </head>␍ <body>␍ <div class="container">␍ <div class="title" align="center">Posting a Flipnote</div>␍ <div class="news-body">␍ <p>Posting a flipnote is as easy as pressing the button down below and selecting the flipnote you want to upload!</p>␍ </div>␍ </div>␍ </body>␍</html>
Binary file not shown.
+72
View File
@@ -0,0 +1,72 @@
<?xml version='1.0' encoding='UTF-8'?>
<ugo_xml>
<layout>
<value>0</value>
</layout>
<title>
<label />
<label />
<label />
<label />
<label />
<num>0</num>
</title>
<button>
<label>All Flipnotes</label>
<address>http://flipnote.hatena.com/ds/v2-eu/frontpage/hotmovies.uls</address>
<trait>100</trait>
<value />
<value>0</value>
</button>
<button>
<label>Announcement</label>
<address>http://flipnote.hatena.com/ds/v2-eu/information.uls</address>
<trait>102</trait>
<value />
<value>0</value>
</button>
<button>
<label>Channels</label>
<address>http://flipnote.hatena.com/ds/v2-eu/channels.uls</address>
<trait>101</trait>
<value />
<value>0</value>
</button>
<button>
<label>News</label>
<address>http://flipnote.hatena.com/ds/v2-eu/help/news.htm?mode=archives</address>
<trait>0</trait>
<value />
<value>0</value>
<embedded_file>__ntfts\News.ntft</embedded_file>
</button>
<button>
<label>Creator's Room</label>
<address>http://flipnote.hatena.com/ds/v2-eu/96FAD88031C49DE1/profile.htm?pm=80&amp;t=260</address>
<trait>104</trait>
<value />
<value>0</value>
</button>
<button>
<label>Special Room</label>
<address>http://flipnote.hatena.com/ds/v2-eu/special_room.htm</address>
<trait>1</trait>
<value />
<value>0</value>
<embedded_file>__ntfts\Special Room.ntft</embedded_file>
</button>
<button>
<label>Help</label>
<address>http://flipnote.hatena.com/ds/v2-eu/help/index_ww.htm</address>
<trait>117</trait>
<value />
<value>0</value>
</button>
<button>
<label>Command Search</label>
<address>ugomemo://command</address>
<trait>103</trait>
<value />
<value>0</value>
</button>
</ugo_xml>
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
from twisted.web import resource
import os
from hatena import Database, Log, ServerLog, Silent, NotFound
detailsHMTL = """uguu"""
#The movie folder:
class PyResource(resource.Resource):
isLeaf = False
def __init__(self):
resource.Resource.__init__(self)
self.CreatorID = CreatorID()
def getChild(self, name, request):
if Database.CreatorExists(name):
return self.CreatorID
elif name == "":
return self
else:
return NotFound
def render(self, request):
request.setResponseCode(403)
return "403 - Denied access"
#The creator ID folder:
class CreatorID(resource.Resource):
isLeaf = False
def __init__(self):
resource.Resource.__init__(self)
self.CreatorIDFile = CreatorIDFile()
def getChild(self, name, request):
CreatorID = request.path.split("/")[-2]
filename = ".".join(name.split(".")[:-1])
if Database.FlipnoteExists(CreatorID, filename):#html, ppm and info
return self.CreatorIDFile
elif name == "":
return self
else:
return NotFound
def render(self, request):
request.setResponseCode(403)
return "403 - Denied access"
#Any public file inside creator ID folder:
class CreatorIDFile(resource.Resource):
isLeaf = True
def __init__(self):
resource.Resource.__init__(self)
def render(self, request):
creator, file = request.path.split("/")[-2:]
filetype = file.split(".")[-1].lower()
if filetype == "ppm":
#log it:
path = "/".join(request.path.split("/")[3:])
Log(request, path)
#add a view:
Database.AddView(creator, file[:-4])
#read ppm file:
f = open(Database.FlipnotePath(creator, file), "rb")
data = f.read()
f.close()
#send file to client:
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return data
elif filetype == "info":
path = "/".join(request.path.split("/")[3:])
Log(request, path)
request.responseHeaders.setRawHeaders('content-type', ['text/plain'])
return "0\n0\n"#undocumented what it means
elif filetype == "htm":#not yet implemented
#ret = detailsHMTL[:]
#ret.replace("%%CreatorID%%", creator)
#ret.replace("%%Filename%%", file[:-4])#without the ext
#ret.replace("%%%%", )
#ret.replace("%%%%", )
return "Not yet implemented"
else:
path = "/".join(request.path.split("/")[3:])
ServerLog.write("%s got 403 when requesting " % (request.getClientIP(), path), Silent)
request.setResponseCode(403)
return "403 - Denied access"
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
from twisted.web import resource
from twisted.internet import reactor
import time
from hatena import Database, ServerLog, Silent
from Hatenatools import TMB
#Handle flipnote uploads
class PyResource(resource.Resource):
isLeaf = True
def render_GET(self, request):
ServerLog.write("%s got 403 when requesting post/flipnote.post with GET" % (request.getClientIP()), Silent)
request.setResponseCode(405)
return "405 - Method Not Allowed"
def render_POST(self, request):#implement channels?
data = request.content.read()
channel = ""
if "channel" in request.args:
channel = request.args["channel"][0]
add = Database.AddFlipnote(data, channel)
if add:
ServerLog.write("%s successfully uploaded \"%s.ppm\"" % (request.getClientIP(), add[1]), Silent)
request.setResponseCode(200)
else:
ServerLog.write("%s tried to upload a flipnote, but failed..." % request.getClientIP(), Silent)
request.setResponseCode(500)#only causes an error, need to fix
return ""
#===
+1
View File
@@ -0,0 +1 @@
���������3욎욎�3�������3욎욎�3�����������������������������������������������3욎욎�3�������3욎욎�3�����������������������������������������������3욎욎�3�������3욎욎�3��������������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎����3욎욎욎욎욎�3����3욎�����������������������������������������3욎욎욎욎욎욎욎욎욎욎욎�3�����������������������������������������3욎욎욎욎욎욎욎욎욎욎욎�3�����������������������������������������3욎욎욎욎욎욎욎욎욎욎욎�3��������������������������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�����������������������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�����������������������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�����������������������������������3욎욎�3�������������������3욎욎�3�����������������������������������3욎욎�3�������������������3욎욎�3�����������������������������������3욎욎�3�������������������3욎욎�3��������������������������������3욎욎욎욎�������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3����3욎욎욎욎욎�3�������3욎욎욎욎욎�3�����������������������������������3욎욎욎욎욎�3�������3욎욎욎욎욎�3�����������������������������������3욎욎욎욎욎�3�������3욎욎욎욎욎�3��������������������������������3욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎욎�3�
+1
View File
@@ -0,0 +1 @@
������������������2�2�2�2����2�2�2�2�2�2��2�2�2���2�2�2�2�2�����2�2�2�2�����2�2�2�2�2���2�2�2��2�2�2�2�2�2����2�2�2�2�����2�2�2�2�����2�2�2�2�����2�2�2�2�����2�2�2�2�����2�2�2�2�����2�2�2�2���
+1
View File
@@ -0,0 +1 @@
���™�5�5�5�5�5�5�5�5�������™��γ?Οό�ό�?Ο?Ο?Ο?Ο?Ο5�5�����™��γ?Ο?Ο?Οό�?Ο�γ�γ?Ο?Ο?Ο5����ό�ό�ό�ό�ό�?Ο?Ο�γ�γ�γ�γ�γ?Ο5���™��γ?Ο?Ο?Οό�?Ο?Ο?Ο?Ο?Ο�γ�γ5���ό�ό�ό�ό�ό�?Ο?Οό�ό�ό�?Ο�γ�γ™���™��γ?Ο?Ο?Οό�ό�?Ο?Ο?Ο?Ο�γ�γ™���ό�ό�ό�ό�ό�?Ο?Ο?Ο�γ�γ�γ?Ο?Ο5���™��γ�γ�γ�γό�?Ο�γ�γ�γ?Ο?Ο5�����™�™�™�™�5�?Ο?Ο?Ο?Ο5�5����������5�?Ο?Ο?Ο5�����������™�?Ο?Ο?Ο5�����������™��γ?Ο?Ο5������������™��γ?Ο?Ο5������������™��γ�γ�γ™�������������™�™�™��������
@@ -0,0 +1,3 @@
[05:04:23] Server startup...
[05:04:25] Server shutdown
Whitespace-only changes.
+103
View File
@@ -0,0 +1,103 @@
from twisted.web import server#filehost
from twisted.internet import reactor
import sys, time, os
#Logging
class Log:
class filesplit:#a file object writing to two outputs
def __init__(self):
self.files = []
def write(self, data):
for i in self.files: i.write(data)
def __init__(self):
minutes, seconds = map(int, time.strftime("%M %S").split(" "))
minutes = 59 - minutes
seconds = 59 - seconds
reactor.callLater(60*minutes + seconds + 5, self.HandleUpdate)
reactor.callLater(60*5, self.AutoFlush)
#make year folder
if not os.path.exists("logs/"+time.strftime("%Y")):
os.mkdir("logs/"+time.strftime("%Y"))
#make month folder
if not os.path.exists("logs/"+time.strftime("%Y/%B")):
os.mkdir("logs/"+time.strftime("%Y/%B"))
self.Activityhandle = open(time.strftime("logs/%Y/%B/%d %B activity.log"),"a")
self.Errorhandle = open(time.strftime("logs/%Y/%B/%d %B error.log"),"a")
self.stderr = sys.stderr
sys.stderr = self.filesplit()
sys.stderr.files.append(self.stderr)
sys.stderr.files.append(self.Errorhandle)
self.write("Server startup...\n", True)
def HandleUpdate(self):#Automatically updates the handles for new filenames every hour
reactor.callLater(60*60, self.HandleUpdate)
print time.strftime("[%H:%M:%S] Handle update")
#make year folder
if not os.path.exists("logs/"+time.strftime("%Y")):
os.mkdir("logs/"+time.strftime("%Y"))
#make month folder
if not os.path.exists("logs/"+time.strftime("%Y/%B")):
os.mkdir("logs/"+time.strftime("%Y/%B"))
self.close()
self.Activityhandle = open(time.strftime("logs/%Y/%B/%d %B activity.log"),"a")
self.Errorhandle = open(time.strftime("logs/%Y/%B/%d %B error.log"),"a")
sys.stderr.files[1] = self.Errorhandle
def AutoFlush(self):#Automatically flushes the files 5 minutes between
reactor.callLater(60*5, self.AutoFlush)
self.flush()
def flush(self):#Flushes to the files
self.Activityhandle.flush()
os.fsync(self.Activityhandle.fileno())
self.Errorhandle.flush()
os.fsync(self.Errorhandle.fileno())
def close(self):
self.Activityhandle.close()
self.Errorhandle.close()
#=====
def write(self, String, Silent=False):
if not Silent:
print time.strftime("[%H:%M:%S]"), String
self.Activityhandle.write(time.strftime("[%H:%M:%S] ") + String + "\n")
Print = write
Log = Log()
#Setup hatena server:
print "Setting up flipnote database..."
import hatena
hatena.ServerLog = Log
print "Setting up hatena site structure..."
site = server.Site(hatena.Setup())
#make the hatena server accept proxy coneections:
print "Setting up proxy hack..."
silent = True
old_lineReceived = site.protocol.lineReceived
def lineReceived(self, line):
if not silent: print line
if line[:31] == "GET http://flipnote.hatena.com/":
line = "GET " + line[30:]
if not silent: print line
elif line[:32] == "POST http://flipnote.hatena.com/":
line = "POST " + line[31:]
if not silent: print line
old_lineReceived(self, line)
site.protocol.lineReceived = lineReceived
#run!
print "Server start!\n"
reactor.listenTCP(8080, site)#Hey listen!~
reactor.run()
#dunn
Log.write("Server shutdown", True)