init
This commit is contained in:
36
tools/ImgToCArray.py
Normal file
36
tools/ImgToCArray.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#converts an image to a C array with the given name
|
||||
#width and height are saved in <name>_w and <name>_h
|
||||
|
||||
import sys
|
||||
import Image
|
||||
|
||||
def emitDataArray(varname, arr, perLine=12):
|
||||
size = len(arr)
|
||||
l = ['unsigned char %s[%d] = {\n'%(varname, size)]
|
||||
for (z, ch) in enumerate(arr):
|
||||
l.append(("%s,"%hex(ord(ch))))
|
||||
#l.append(("%d,"%ord(ch)))
|
||||
if (z+1) % perLine == 0: l.append("\n");
|
||||
l.append("\n};")
|
||||
return "".join(l)
|
||||
|
||||
def emitImgDataArray(varname, imgfn, perLine=12):
|
||||
img = Image.open(imgfn)
|
||||
pixdata = list(img.getdata())
|
||||
data = []
|
||||
for pix in pixdata:
|
||||
data.extend( map(chr, pix) )
|
||||
|
||||
l = ["const int %s_W = %d;\nconst int %s_H = %d;\n"%(varname, img.size[0], varname, img.size[1])]
|
||||
l.append( emitDataArray(varname, "".join(data)) )
|
||||
return "\n".join(l)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
fn = sys.argv[1]
|
||||
varname = sys.argv[2]
|
||||
except:
|
||||
print("Usage: %s <img-filename> <out-variable-name>"%sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
print emitImgDataArray( varname, fn )
|
||||
37
tools/ImgToTex.py
Normal file
37
tools/ImgToTex.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#converts an image to a C array with the given name
|
||||
#width and height are saved in <name>_w and <name>_h
|
||||
|
||||
import sys
|
||||
import Image
|
||||
import array
|
||||
|
||||
def emitImageTex(imgfn, outfn):
|
||||
img = Image.open(imgfn).convert("RGBA")
|
||||
pixdata = list(img.getdata())
|
||||
data = []
|
||||
for pix in pixdata:
|
||||
data.extend( map(chr, pix) )
|
||||
|
||||
fp = open(outfn, "wb")
|
||||
header = array.array("i", [img.size[0], img.size[1], 0])
|
||||
header.tofile(fp)
|
||||
|
||||
arr = array.array("c", data)
|
||||
arr.tofile(fp)
|
||||
|
||||
fp.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
||||
try:
|
||||
fn = sys.argv[1]
|
||||
try:
|
||||
fnout = sys.argv[2]
|
||||
except:
|
||||
fnout = os.path.splitext(fn)[0] + ".tex"
|
||||
except:
|
||||
print("Usage: %s <img-filename> <out-filename>"%sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
emitImageTex( fn, fnout )
|
||||
154
tools/JConvert.py
Normal file
154
tools/JConvert.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import re, rehelper as h, path
|
||||
|
||||
SUB = 1
|
||||
IF_PARAMETER = 11
|
||||
FUNC_PER_ROW = 101
|
||||
FUNC_PER_FILE = 102
|
||||
|
||||
class Params:
|
||||
def __init__(self, regexprules, *other):
|
||||
self.regexps = []
|
||||
self.other = other
|
||||
|
||||
self.addRegexps(regexprules)
|
||||
|
||||
def addRegexps(self, rules):
|
||||
self.regexps.extend(rules)
|
||||
|
||||
def GetPackageInfo(x):
|
||||
try:
|
||||
pkg = re.search("package (.*?);", x, re.M)
|
||||
return pkg.span()[0], pkg.groups()[0].split(".")
|
||||
except: return -1, []
|
||||
|
||||
def ImportToInclude(env, x):
|
||||
if not x.startswith("import"):
|
||||
return x
|
||||
|
||||
x = x.rstrip().rstrip(";")
|
||||
if x.endswith("*"):
|
||||
return "/* " + x + " */"
|
||||
|
||||
packages = x[7:].replace(".", "/")
|
||||
basepath = "/".join(env['package'])
|
||||
relpath = path.Path(basepath).relpathto(packages).replace("\\", "/")
|
||||
|
||||
"""
|
||||
print packages
|
||||
print basepath
|
||||
print relpath
|
||||
raw_input()
|
||||
"""
|
||||
|
||||
return '#include "' + relpath + '.h"';
|
||||
|
||||
def PackageToGuards(x):
|
||||
try:
|
||||
b, pkg = GetPackageInfo(x)
|
||||
if b == -1: raise Exception("Package not found")
|
||||
package = "_".join(pkg)
|
||||
x = x[:b] + "//" + x[b:]
|
||||
clazz = re.search("(class|interface) (%s)"%h.identifier(), x, re.M).groups()[1]
|
||||
full = "%s__%s_H__"%(package.upper(), clazz)
|
||||
return "#ifndef %s\n#define %s\n\n%s\n#endif /*%s*/\n"%(full,full,x,full)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return x
|
||||
|
||||
def convert(infilename, outfilename, params):
|
||||
ins = outs = file(infilename, 'r').read()
|
||||
env = {"package": GetPackageInfo(outs)[1]}
|
||||
|
||||
for ot, of in params.other:
|
||||
if ot == FUNC_PER_ROW:
|
||||
l = outs = "\n".join([of(env, line) for line in outs.split("\n")])
|
||||
|
||||
if ot == FUNC_PER_FILE: outs = of(outs)
|
||||
|
||||
def _ifParameter(dst):
|
||||
def checkIfParam(b, e):
|
||||
eaten = []
|
||||
for x in outs[:b][::-1]:
|
||||
if x == "(": break
|
||||
if x in "){}": return False
|
||||
eaten.append(x)
|
||||
for x in outs[e+1:]:
|
||||
if x == ")": break
|
||||
if x in "({}": return False
|
||||
eaten.append(x)
|
||||
return "=" not in eaten
|
||||
return lambda m: dst if checkIfParam(*m.span()) else m.group()
|
||||
F = {IF_PARAMETER : _ifParameter
|
||||
}
|
||||
for rexp in params.regexps:
|
||||
type, src, dst = rexp[:3]
|
||||
try: dstFunc = F.get(rexp[3], None)(dst)
|
||||
except: dstFunc = None
|
||||
if type == SUB: outs = re.sub(src, dstFunc or dst, outs)
|
||||
|
||||
file(outfilename, 'w').write(outs)
|
||||
|
||||
def compileRegexpRules(ruleList): return ruleList
|
||||
|
||||
JavaToCppRules = compileRegexpRules([
|
||||
(SUB, h.wholeWord("boolean"), "bool"),
|
||||
(SUB, h.wholeWord("double"), "float"),
|
||||
(SUB, h.wholeWord("byte"), "char"),
|
||||
(SUB, h.wholeWord("String"), "const std::string&", IF_PARAMETER),
|
||||
(SUB, h.wholeWord("String"), "std::string"),
|
||||
(SUB, h.wholeWord("null"), "NULL"),
|
||||
(SUB, h.wholeWord("private"), "/*private*/"),
|
||||
(SUB, h.wholeWord("protected"), "/*protected*/"),
|
||||
(SUB, h.wholeWord("public"), "/*public*/"),
|
||||
(SUB, h.wholeWord("interface"), "/*interface*/ class"),
|
||||
(SUB, h.wholeWord("final"), "const"),
|
||||
(SUB, h.wholeWord("new"), "/*new*/"),
|
||||
(SUB, h.wholeWord(" implements "),": /*implements-interface*/ public "),
|
||||
(SUB, h.wholeWord(" extends "),": public "),
|
||||
(SUB, "@Override", "/*@Override*/"),
|
||||
(SUB, "\\bthis[.]", "this->"),
|
||||
(SUB, "\\bsuper[.]","super::"),
|
||||
(SUB, "\\btag[.]","tag->"),
|
||||
(SUB, "\\bentityTag[.]","entityTag->")
|
||||
])
|
||||
|
||||
JavaToCppParams = Params(JavaToCppRules,
|
||||
(FUNC_PER_ROW, ImportToInclude),
|
||||
(FUNC_PER_FILE,PackageToGuards))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os, sys
|
||||
import glob
|
||||
|
||||
MinecraftRules = compileRegexpRules([
|
||||
(SUB, "\\blevel[.]","level->"),
|
||||
(SUB, "\\bentity[.]","entity->"),
|
||||
(SUB, "\\bplayer[.]","player->"),
|
||||
(SUB, h.wholeWord("Mob"), "Mob*", IF_PARAMETER),
|
||||
(SUB, h.wholeWord("Player"), "Player*", IF_PARAMETER),
|
||||
(SUB, h.wholeWord("Tile"), "Tile*", IF_PARAMETER),
|
||||
(SUB, h.wholeWord("Level"), "Level*", IF_PARAMETER),
|
||||
(SUB, h.wholeWord("Entity"), "Entity*", IF_PARAMETER),
|
||||
(SUB, "\\bTile[.](%s)[.]"%h.identifier(),"Tile::\\1->"),
|
||||
(SUB, "\\bItem[.](%s)[.]"%h.identifier(),"Item::\\1->"),
|
||||
(SUB, "\\b[^/]?Tile[.]","Tile::"),
|
||||
(SUB, "\\b[^/]?Item[.]","Item::"),
|
||||
(SUB, "\\b[^/]?Material[.]","Material::"),
|
||||
(SUB, "\\b[^/]?Mth[.]","Mth::")
|
||||
])
|
||||
|
||||
# Java to C++
|
||||
params = JavaToCppParams
|
||||
# add more params here if needed
|
||||
params.addRegexps(MinecraftRules)
|
||||
|
||||
fns = glob.glob(sys.argv[1])
|
||||
for full in fns:
|
||||
fn, ext = os.path.splitext(full)
|
||||
outfn = "%s.h"%fn
|
||||
|
||||
if os.path.exists(outfn):
|
||||
continue
|
||||
|
||||
convert("%s.java"%fn, outfn, params)
|
||||
39
tools/WavToPCM.py
Normal file
39
tools/WavToPCM.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import struct
|
||||
import wave
|
||||
|
||||
from ImgToCArray import emitDataArray
|
||||
|
||||
def conv(fn):
|
||||
wav = wave.Wave_read(fn)
|
||||
|
||||
nframes = wav.getnframes()
|
||||
|
||||
header = struct.pack("iiii",
|
||||
wav.getnchannels(),
|
||||
wav.getsampwidth(),
|
||||
wav.getframerate(),
|
||||
nframes)
|
||||
|
||||
frames = wav.readframes(nframes)
|
||||
|
||||
rawname = os.path.splitext(fn)[0]
|
||||
cdata = emitDataArray("PCM_%s"%rawname, header + frames)
|
||||
|
||||
out = file("%s.pcm"%rawname, 'wb')
|
||||
out.write(cdata)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys, glob
|
||||
|
||||
try: arg = sys.argv[1]
|
||||
except:
|
||||
print "usage: wavToRaw.py <glob-pattern|filename>"
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
fns = glob.glob(arg)
|
||||
if not fns: fns = [arg]
|
||||
|
||||
for fn in fns:
|
||||
conv(fn)
|
||||
93
tools/blockplacer.py
Normal file
93
tools/blockplacer.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import Image
|
||||
|
||||
class TileReader:
|
||||
def __init__(self, im, size):
|
||||
self.im = im
|
||||
self.size = size
|
||||
|
||||
def isEmpty(self, x, y):
|
||||
# if x == y == 0:
|
||||
# print "LOL"
|
||||
x *= self.size
|
||||
y *= self.size
|
||||
for yo in range(self.size):
|
||||
for xo in range(self.size):
|
||||
try:
|
||||
p = self.im.getpixel((x+xo, y+yo))
|
||||
psum = sum(p)
|
||||
if psum > 0 and p != (255, 0, 255): return False
|
||||
except:
|
||||
print self.size, x+xo, y+yo
|
||||
return True
|
||||
|
||||
def getPoint(self, x, y):
|
||||
return x*self.size, y*self.size
|
||||
|
||||
def getBox(self, x, y):
|
||||
x0, y0 = self.getPoint(x, y)
|
||||
x1, y1 = self.getPoint(x+1, y+1)
|
||||
return x0,y0,x1,y1
|
||||
|
||||
|
||||
def buildImage(listDef, blockImg, iconImg, terrainImg):
|
||||
ss = [48, 16, 16]
|
||||
ts = [TileReader(blockImg, ss[0]), TileReader(iconImg, ss[1]), TileReader(terrainImg, ss[2])]
|
||||
ks = [int( 512 / x ) for x in ss]
|
||||
ix = [-1, ks[1]*3*8 -1]
|
||||
|
||||
out = Image.new("RGBA", (512, 512), (0,0,0,0))
|
||||
|
||||
n = 0
|
||||
map = {}
|
||||
|
||||
for yy in range(blockImg.size[1] / ss[0]):
|
||||
for xx in range(16):#1 + (im.size[0]-1)/48):
|
||||
if n >= len(listDef):
|
||||
break
|
||||
|
||||
type,b,c,d,e = listDef[n]
|
||||
ix[type] += 1
|
||||
ii = ix[type]
|
||||
|
||||
x,y = xx, yy
|
||||
if type == 1:
|
||||
x = e%16
|
||||
y = e/16
|
||||
if c < 256: type = 2
|
||||
|
||||
sz= ss[type]
|
||||
t = ts[type]
|
||||
k = ks[type]
|
||||
im =t.im
|
||||
|
||||
if not t.isEmpty(x, y):
|
||||
box = t.getBox(x, y)
|
||||
part = im.crop(box)
|
||||
|
||||
ppt = (sz*(ii%k), sz*(ii/k))
|
||||
part.save("t/%s.png"%n)
|
||||
out.paste(part, ppt)
|
||||
n += 1
|
||||
|
||||
for y in range(out.size[1]):
|
||||
for x in range(out.size[0]):
|
||||
p = r,g,b,a = out.getpixel((x, y))
|
||||
if p == (255,0,255,255):
|
||||
out.putpixel((x, y), (0, 0, 0, 0))
|
||||
return out
|
||||
|
||||
def run(listDef, fn):
|
||||
im = Image.open(fn)
|
||||
im2 = Image.open("items.png")
|
||||
im3 = Image.open("terrain.png")
|
||||
|
||||
buildImage(listDef, im, im2, im3).save("genblocks.png")
|
||||
|
||||
"""
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
try: fn = sys.argv[1]
|
||||
except: fn = "blocks_alpha.png"
|
||||
|
||||
# run(0, fn)
|
||||
"""
|
||||
197
tools/guiblockgen.py
Normal file
197
tools/guiblockgen.py
Normal file
@@ -0,0 +1,197 @@
|
||||
rl = """1, 0, 273, 0, 81
|
||||
1, 1, 274, 0, 97
|
||||
1, 2, 275, 0, 113
|
||||
1, 3, 359, 0, 93
|
||||
1, 4, 272, 0, 65
|
||||
1, 5, 65, 0, 83
|
||||
1, 6, 50, 0, 80
|
||||
1, 7, 324, 0, 43
|
||||
0, 8, 85, 0, 0
|
||||
0, 9, 107, 0, 0
|
||||
0, 10, 4, 0, 0
|
||||
0, 11, 17, 1, 0
|
||||
0, 12, 17, 2, 0
|
||||
0, 13, 5, 0, 0
|
||||
0, 14, 45, 0, 0
|
||||
0, 15, 3, 0, 0
|
||||
0, 16, 24, 0, 0
|
||||
0, 17, 13, 0, 0
|
||||
0, 18, 1, 0, 0
|
||||
0, 19, 67, 0, 0
|
||||
0, 20, 53, 0, 0
|
||||
0, 21, 108, 0, 0
|
||||
0, 22, 44, 0, 0
|
||||
0, 23, 44, 2, 0
|
||||
0, 24, 44, 4, 0
|
||||
0, 25, 12, 0, 0
|
||||
0, 26, 35, 7, 0
|
||||
0, 27, 35, 6, 0
|
||||
0, 28, 35, 5, 0
|
||||
0, 29, 35, 4, 0
|
||||
0, 30, 35, 3, 0
|
||||
0, 31, 35, 2, 0
|
||||
0, 32, 35, 1, 0
|
||||
0, 33, 35, 15, 0
|
||||
0, 34, 35, 14, 0
|
||||
0, 35, 35, 13, 0
|
||||
0, 36, 35, 12, 0
|
||||
0, 37, 35, 11, 0
|
||||
0, 38, 35, 10, 0
|
||||
0, 39, 35, 9, 0
|
||||
0, 40, 35, 8, 0
|
||||
0, 41, 20, 0, 0
|
||||
0, 42, 18, 0, 0
|
||||
0, 43, 41, 0, 0
|
||||
0, 44, 42, 0, 0
|
||||
0, 45, 57, 0, 0
|
||||
0, 46, 49, 0, 0
|
||||
0, 47, 47, 0, 0
|
||||
0, 48, 58, 0, 0
|
||||
0, 49, 61, 0, 0
|
||||
1, 50, 37, 0, 13
|
||||
1, 51, 38, 0, 12
|
||||
1, 52, 39, 0, 29
|
||||
1, 53, 40, 0, 28
|
||||
1, 54, 81, 0, 70
|
||||
1, 55, 83, 0, 73
|
||||
0, 56, 15, 0, 0
|
||||
0, 57, 14, 0, 0
|
||||
0, 58, 56, 0, 0
|
||||
0, 59, 21, 0, 0
|
||||
1, 60, 266, 0, 39
|
||||
1, 61, 265, 0, 23
|
||||
0, 62, 17, 0, 0
|
||||
1, 63, 263, 1, 7
|
||||
1, 64, 264, 0, 55
|
||||
1, 65, 351, 2, 110
|
||||
1, 66, 337, 0, 57
|
||||
1, 67, 336, 0, 22
|
||||
1, 68, 270, 0, 96
|
||||
1, 69, 280, 0, 53
|
||||
1, 70, 269, 0, 80
|
||||
1, 71, 271, 0, 112
|
||||
1, 72, 257, 0, 98
|
||||
1, 73, 256, 0, 82
|
||||
1, 74, 258, 0, 114
|
||||
1, 75, 278, 0, 99
|
||||
1, 76, 277, 0, 83
|
||||
1, 77, 279, 0, 115
|
||||
1, 78, 285, 0, 100
|
||||
1, 79, 284, 0, 84
|
||||
1, 80, 286, 0, 116
|
||||
1, 81, 268, 0, 64
|
||||
1, 82, 267, 0, 66
|
||||
1, 83, 276, 0, 67
|
||||
1, 84, 283, 0, 68
|
||||
0, 85, 22, 0, 0
|
||||
1, 86, 351, 4, 142
|
||||
1, 87, 102, 0, 49
|
||||
0, 88, 35, 0, 0
|
||||
1, 89, 351, 11, 12
|
||||
1, 90, 339, 0, 58
|
||||
1, 91, 338, 0, 27
|
||||
1, 92, 340, 0, 59
|
||||
0, 93, 80, 0, 0
|
||||
1, 94, 332, 0, 14
|
||||
0, 95, 82, 0, 0
|
||||
0, 96, 44, 3, 0
|
||||
1, 97, 353, 0, 13
|
||||
1, 98, 263, 0, 7
|
||||
1, 99, 281, 0, 71
|
||||
1, 100, 6, 2, 79
|
||||
1, 101, 6, 1, 63
|
||||
1, 102, 6, 0, 15"""
|
||||
|
||||
class T:
|
||||
def __init__(self):
|
||||
self.t = {}
|
||||
def set(self, i, id, data):
|
||||
if not id in self.t: self.t[id] = []
|
||||
self.t[id].append((i,data))
|
||||
def _getType(self, id):
|
||||
return len(self.t.get(id, []))
|
||||
def isAvailable(self, id):
|
||||
return 0 != self._getType(id)
|
||||
def isUnique(self, id):
|
||||
return 1 == self._getType(id)
|
||||
def isMultiple(self, id):
|
||||
return 2 <= self._getType(id)
|
||||
def hasDataFor(self, id, data):
|
||||
if not self.isAvailable(id): return False
|
||||
return data in self.t.get(id, [(0,0)])
|
||||
def getIndexFor(self, id, data):
|
||||
for (i, d) in self.t[id]:
|
||||
if data == d:
|
||||
return i
|
||||
return -1
|
||||
|
||||
def getCArrayFormat(l):
|
||||
#if len(l) < 20:
|
||||
return "{" + ", ".join(map(str,l)) + "};"
|
||||
s = "{\n"
|
||||
n = 0
|
||||
for x in l:
|
||||
if n == 0: s += " ";
|
||||
s += "%3d,"%x
|
||||
n += 1
|
||||
if n == 10:
|
||||
s+= "\n"
|
||||
n = 0
|
||||
s += "};"
|
||||
return s
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def emitDefinition(id, data):
|
||||
typeId = "static const signed short"
|
||||
return "%s _%s[] = %s\n"%(typeId,id,getCArrayFormat(data))
|
||||
|
||||
ll = [map(int, k.split(", ")) for k in [x.strip() for x in rl.split("\n") if x.strip()]]
|
||||
|
||||
import blockplacer
|
||||
blockplacer.run(ll, "blocks.png")
|
||||
|
||||
types = {}
|
||||
|
||||
ts = [T(), T()]
|
||||
index = [-1, 128-1]
|
||||
maxId = 0
|
||||
for (type, i, id, data, i2) in ll:
|
||||
index[type] += 1
|
||||
ts[type].set(index[type], id, data)
|
||||
if id > maxId: maxId = id
|
||||
|
||||
NumItems = maxId + 1
|
||||
handled = {}
|
||||
l = [-1] * NumItems # -1 is HAS_NOT, -2 is RE-QUERY, 0 <= x < 128 is
|
||||
# block index, x >= 128 is icon index
|
||||
q = []
|
||||
for (type, i, id, data, i2) in ll:
|
||||
# type == 0: Rendered Tile. Top left 480 * 432 pixels are allocated for those.
|
||||
# type == 1: Icon. Index start on 128
|
||||
|
||||
if id >= NumItems: continue
|
||||
|
||||
t = ts[type]
|
||||
if id == 38:
|
||||
print type, i, id, data, i2,
|
||||
|
||||
if t.isUnique(id):
|
||||
l[id] = t.getIndexFor(id, data)
|
||||
if t.isMultiple(id) and id not in handled:
|
||||
l[id] = -2
|
||||
handled[id] = True
|
||||
m = []
|
||||
for data in range(16): # data
|
||||
index = t.getIndexFor(id, data)
|
||||
m.append(index)
|
||||
q.append( (id, emitDefinition(id, m)))
|
||||
|
||||
import sys
|
||||
f = file("def.cpp", 'w')
|
||||
sys.stdout = f
|
||||
|
||||
for (id, datalist) in q:
|
||||
print datalist
|
||||
print emitDefinition("mapper", l)
|
||||
#print l[45]
|
||||
839
tools/path.py
Normal file
839
tools/path.py
Normal file
@@ -0,0 +1,839 @@
|
||||
""" path.py - An object representing a path to a file or directory.
|
||||
|
||||
Example:
|
||||
|
||||
from os.path import Path
|
||||
d = Path('/home/guido/bin')
|
||||
for f in d.files('*.py'):
|
||||
f.chmod(0755)
|
||||
|
||||
Author: Jason Orendorff <jason@jorendorff.com> (and others)
|
||||
Date: 7 Mar 2004
|
||||
|
||||
Adapted for stdlib by: Reinhold Birkenfeld, July 2005
|
||||
"""
|
||||
|
||||
|
||||
# TODO
|
||||
# - Bug in write_text(). It doesn't support Universal newline mode.
|
||||
# - Better error message in listdir() when self isn't a
|
||||
# directory. (On Windows, the error message really sucks.)
|
||||
# - Add methods for regex find and replace.
|
||||
# - guess_content_type() method?
|
||||
|
||||
# TODO (standard module)
|
||||
# - add more standard header?
|
||||
# - Make sure everything has a good docstring.
|
||||
|
||||
|
||||
import sys, os, fnmatch, glob, shutil, codecs
|
||||
|
||||
__version__ = "$Id$"
|
||||
__all__ = ['Path']
|
||||
|
||||
|
||||
# Universal newline support
|
||||
_textmode = 'r'
|
||||
if hasattr(file, 'newlines'):
|
||||
_textmode = 'U'
|
||||
|
||||
if os.path.supports_unicode_filenames:
|
||||
_base = unicode
|
||||
else:
|
||||
_base = str
|
||||
|
||||
class Path(_base):
|
||||
""" Represents a filesystem path.
|
||||
|
||||
Path is an immutable object.
|
||||
|
||||
For documentation on individual methods, consult their
|
||||
counterparts in os.path.
|
||||
"""
|
||||
|
||||
# --- Special Python methods.
|
||||
|
||||
def __new__(typ, *args):
|
||||
""" Initialize a Path instance.
|
||||
|
||||
The argument can be either a string or an existing Path object.
|
||||
"""
|
||||
if not args:
|
||||
return typ(os.curdir)
|
||||
for arg in args:
|
||||
if not isinstance(arg, basestring):
|
||||
raise ValueError("%s() arguments must be Path, str or unicode" % typ.__name__)
|
||||
if len(args) == 1:
|
||||
return _base.__new__(typ, *args)
|
||||
else:
|
||||
return typ(os.path.join(*args))
|
||||
|
||||
# Iterating over a string yields its parts
|
||||
def __iter__(self):
|
||||
return iter(self.parts())
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, _base(self))
|
||||
|
||||
def base(self):
|
||||
return _base(self)
|
||||
|
||||
# Adding path and string yields a path
|
||||
# Caution: this is not a join!
|
||||
def __add__(self, other):
|
||||
if isinstance(other, basestring):
|
||||
return self.__class__(_base(self) + other)
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other):
|
||||
if isinstance(other, basestring):
|
||||
return self.__class__(other + _base(self))
|
||||
return NotImplemented
|
||||
|
||||
# The / joins paths
|
||||
def __div__(self, other):
|
||||
return self.joinwith(other)
|
||||
|
||||
__truediv__ = __div__
|
||||
|
||||
# Alternative constructor.
|
||||
|
||||
@classmethod
|
||||
def cwd(cls):
|
||||
""" Return the current working directory as a path object. """
|
||||
if os.path.supports_unicode_filenames:
|
||||
return cls(os.getcwdu())
|
||||
else:
|
||||
return cls(os.getcwd())
|
||||
|
||||
|
||||
# --- Operations which return strings
|
||||
|
||||
basename = property(
|
||||
os.path.basename, None, None,
|
||||
""" The name of this file or directory without the full path.
|
||||
|
||||
For example, Path('/usr/local/lib/libpython.so').basename == 'libpython.so'
|
||||
""")
|
||||
|
||||
def _get_namebase(self):
|
||||
base, ext = os.path.splitext(self.basename)
|
||||
return base
|
||||
|
||||
def _get_ext(self):
|
||||
f, ext = os.path.splitext(self)
|
||||
return ext
|
||||
|
||||
def _get_drive(self):
|
||||
drive, r = os.path.splitdrive(self)
|
||||
return drive
|
||||
|
||||
namebase = property(
|
||||
_get_namebase, None, None,
|
||||
""" The same as Path.basename, but with one file extension stripped off.
|
||||
|
||||
For example, Path('/home/guido/python.tar.gz').basename == 'python.tar.gz',
|
||||
but Path('/home/guido/python.tar.gz').namebase == 'python.tar'
|
||||
""")
|
||||
|
||||
ext = property(
|
||||
_get_ext, None, None,
|
||||
""" The file extension, for example '.py'. """)
|
||||
|
||||
drive = property(
|
||||
_get_drive, None, None,
|
||||
""" The drive specifier, for example 'C:'.
|
||||
This is always empty on systems that don't use drive specifiers.
|
||||
""")
|
||||
|
||||
# --- Operations which return Path objects
|
||||
|
||||
def abspath(self):
|
||||
return self.__class__(os.path.abspath(self))
|
||||
|
||||
def normcase(self):
|
||||
return self.__class__(os.path.normcase(self))
|
||||
|
||||
def normpath(self):
|
||||
return self.__class__(os.path.normpath(self))
|
||||
|
||||
def realpath(self):
|
||||
return self.__class__(os.path.realpath(self))
|
||||
|
||||
def expanduser(self):
|
||||
return self.__class__(os.path.expanduser(self))
|
||||
|
||||
def expandvars(self):
|
||||
return self.__class__(os.path.expandvars(self))
|
||||
|
||||
def expand(self):
|
||||
""" Clean up a filename by calling expandvars(),
|
||||
expanduser(), and normpath() on it.
|
||||
|
||||
This is commonly everything needed to clean up a filename
|
||||
read from a configuration file, for example.
|
||||
"""
|
||||
return self.expandvars().expanduser().normpath()
|
||||
|
||||
def _get_directory(self):
|
||||
return self.__class__(os.path.dirname(self))
|
||||
|
||||
directory = property(
|
||||
_get_directory, None, None,
|
||||
""" This path's parent directory, as a new path object.
|
||||
|
||||
For example, Path('/usr/local/lib/libpython.so').directory == Path('/usr/local/lib')
|
||||
""")
|
||||
|
||||
def stripext(self):
|
||||
""" p.stripext() -> Remove one file extension from the path.
|
||||
|
||||
For example, path('/home/guido/python.tar.gz').stripext()
|
||||
returns path('/home/guido/python.tar').
|
||||
"""
|
||||
return self.splitext()[0]
|
||||
|
||||
# --- Operations which return Paths and strings
|
||||
|
||||
def splitpath(self):
|
||||
""" p.splitpath() -> Return (p.directory, p.basename). """
|
||||
parent, child = os.path.split(self)
|
||||
return self.__class__(parent), child
|
||||
|
||||
def splitdrive(self):
|
||||
""" p.splitdrive() -> Return (Path(p.drive), <the rest of p>).
|
||||
|
||||
Split the drive specifier from this path. If there is
|
||||
no drive specifier, p.drive is empty, so the return value
|
||||
is simply (Path(''), p). This is always the case on Unix.
|
||||
"""
|
||||
drive, rel = os.path.splitdrive(self)
|
||||
return self.__class__(drive), rel
|
||||
|
||||
def splitext(self):
|
||||
""" p.splitext() -> Return (p.stripext(), p.ext).
|
||||
|
||||
Split the filename extension from this path and return
|
||||
the two parts. Either part may be empty.
|
||||
|
||||
The extension is everything from '.' to the end of the
|
||||
last path segment.
|
||||
"""
|
||||
filename, ext = os.path.splitext(self)
|
||||
return self.__class__(filename), ext
|
||||
|
||||
if hasattr(os.path, 'splitunc'):
|
||||
def splitunc(self):
|
||||
unc, rest = os.path.splitunc(self)
|
||||
return self.__class__(unc), rest
|
||||
|
||||
def _get_uncshare(self):
|
||||
unc, r = os.path.splitunc(self)
|
||||
return self.__class__(unc)
|
||||
|
||||
uncshare = property(
|
||||
_get_uncshare, None, None,
|
||||
""" The UNC mount point for this path.
|
||||
This is empty for paths on local drives. """)
|
||||
|
||||
def joinwith(self, *args):
|
||||
""" Join two or more path components, adding a separator
|
||||
character (os.sep) if needed. Returns a new path
|
||||
object.
|
||||
"""
|
||||
return self.__class__(os.path.join(self, *args))
|
||||
|
||||
joinpath = joinwith
|
||||
|
||||
def parts(self):
|
||||
""" Return a list of the path components in this path.
|
||||
|
||||
The first item in the list will be a path. Its value will be
|
||||
either os.curdir, os.pardir, empty, or the root directory of
|
||||
this path (for example, '/' or 'C:\\'). The other items in
|
||||
the list will be strings.
|
||||
|
||||
Path(*result) will yield the original path.
|
||||
"""
|
||||
parts = []
|
||||
loc = self
|
||||
while loc != os.curdir and loc != os.pardir:
|
||||
prev = loc
|
||||
loc, child = prev.splitpath()
|
||||
if loc == prev:
|
||||
break
|
||||
parts.append(child)
|
||||
parts.append(loc)
|
||||
parts.reverse()
|
||||
return parts
|
||||
|
||||
def relpath(self):
|
||||
""" Return this path as a relative path,
|
||||
based from the current working directory.
|
||||
"""
|
||||
return self.__class__.cwd().relpathto(self)
|
||||
|
||||
def relpathto(self, dest):
|
||||
""" Return a relative path from self to dest.
|
||||
|
||||
If there is no relative path from self to dest, for example if
|
||||
they reside on different drives in Windows, then this returns
|
||||
dest.abspath().
|
||||
"""
|
||||
origin = self.abspath()
|
||||
dest = self.__class__(dest).abspath()
|
||||
|
||||
orig_list = origin.normcase().parts()
|
||||
# Don't normcase dest! We want to preserve the case.
|
||||
dest_list = dest.parts()
|
||||
|
||||
if orig_list[0] != os.path.normcase(dest_list[0]):
|
||||
# Can't get here from there.
|
||||
return dest
|
||||
|
||||
# Find the location where the two paths start to differ.
|
||||
i = 0
|
||||
for start_seg, dest_seg in zip(orig_list, dest_list):
|
||||
if start_seg != os.path.normcase(dest_seg):
|
||||
break
|
||||
i += 1
|
||||
|
||||
# Now i is the point where the two paths diverge.
|
||||
# Need a certain number of "os.pardir"s to work up
|
||||
# from the origin to the point of divergence.
|
||||
segments = [os.pardir] * (len(orig_list) - i)
|
||||
# Need to add the diverging part of dest_list.
|
||||
segments += dest_list[i:]
|
||||
if len(segments) == 0:
|
||||
# If they happen to be identical, use os.curdir.
|
||||
return self.__class__(os.curdir)
|
||||
else:
|
||||
return self.__class__(os.path.join(*segments))
|
||||
|
||||
|
||||
# --- Listing, searching, walking, and matching
|
||||
|
||||
def listdir(self):
|
||||
return [self.__class__(p) for p in os.listdir(self)]
|
||||
|
||||
def children(self, pattern=None):
|
||||
""" D.children() -> List of items in this directory,
|
||||
with this path prepended to them.
|
||||
|
||||
Use D.files() or D.dirs() instead if you want a listing
|
||||
of just files or just subdirectories.
|
||||
|
||||
The elements of the list are path objects.
|
||||
|
||||
With the optional 'pattern' argument, this only lists
|
||||
items whose names match the given pattern.
|
||||
"""
|
||||
names = os.listdir(self)
|
||||
if pattern is not None:
|
||||
names = fnmatch.filter(names, pattern)
|
||||
return [self / child for child in names]
|
||||
|
||||
def dirs(self, pattern=None):
|
||||
""" D.dirs() -> List of this directory's subdirectories.
|
||||
|
||||
The elements of the list are path objects.
|
||||
This does not walk recursively into subdirectories
|
||||
(but see path.walkdirs).
|
||||
|
||||
With the optional 'pattern' argument, this only lists
|
||||
directories whose names match the given pattern. For
|
||||
example, d.dirs('build-*').
|
||||
"""
|
||||
return [p for p in self.children(pattern) if p.isdir()]
|
||||
|
||||
def files(self, pattern=None):
|
||||
""" D.files() -> List of the files in this directory.
|
||||
|
||||
The elements of the list are path objects.
|
||||
This does not walk into subdirectories (see path.walkfiles).
|
||||
|
||||
With the optional 'pattern' argument, this only lists files
|
||||
whose names match the given pattern. For example,
|
||||
d.files('*.pyc').
|
||||
"""
|
||||
|
||||
return [p for p in self.children(pattern) if p.isfile()]
|
||||
|
||||
def walk(self, pattern=None):
|
||||
""" D.walk() -> iterator over files and subdirs, recursively.
|
||||
|
||||
The iterator yields path objects naming each child item of
|
||||
this directory and its descendants, including the starting
|
||||
path. This requires that D.isdir().
|
||||
|
||||
This performs a depth-first traversal of the directory tree.
|
||||
Each directory is returned just before all its children.
|
||||
"""
|
||||
for child in self.children():
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
if child.isdir():
|
||||
for item in child.walk(pattern):
|
||||
yield item
|
||||
|
||||
def walkdirs(self, pattern=None):
|
||||
""" D.walkdirs() -> iterator over subdirs, recursively.
|
||||
|
||||
With the optional 'pattern' argument, this yields only
|
||||
directories whose names match the given pattern. For
|
||||
example, mydir.walkdirs('*test') yields only directories
|
||||
with names ending in 'test'.
|
||||
"""
|
||||
for child in self.dirs():
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
for subsubdir in child.walkdirs(pattern):
|
||||
yield subsubdir
|
||||
|
||||
def walkfiles(self, pattern=None):
|
||||
""" D.walkfiles() -> iterator over files in D, recursively.
|
||||
|
||||
The optional argument, pattern, limits the results to files
|
||||
with names that match the pattern. For example,
|
||||
mydir.walkfiles('*.tmp') yields only files with the .tmp
|
||||
extension.
|
||||
"""
|
||||
for child in self.children():
|
||||
if child.isfile():
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
elif child.isdir():
|
||||
for f in child.walkfiles(pattern):
|
||||
yield f
|
||||
|
||||
def fnmatch(self, pattern):
|
||||
""" Return True if self.name matches the given pattern.
|
||||
|
||||
pattern - A filename pattern with wildcards,
|
||||
for example '*.py'.
|
||||
"""
|
||||
return fnmatch.fnmatch(self.basename, pattern)
|
||||
|
||||
def glob(self, pattern):
|
||||
""" Return a list of path objects that match the pattern.
|
||||
|
||||
pattern - a path relative to this directory, with wildcards.
|
||||
|
||||
For example, path('/users').glob('*/bin/*') returns a list
|
||||
of all the files users have in their bin directories.
|
||||
"""
|
||||
return map(self.__class__, glob.glob(self / pattern))
|
||||
|
||||
|
||||
# --- Reading or writing an entire file at once.
|
||||
|
||||
def open(self, mode='r'):
|
||||
""" Open this file. Return a file object. """
|
||||
return file(self, mode)
|
||||
|
||||
def read_file_bytes(self):
|
||||
""" Open this file, read all bytes, return them as a string. """
|
||||
f = self.open('rb')
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def write_file_bytes(self, bytes, append=False):
|
||||
""" Open this file and write the given bytes to it.
|
||||
|
||||
Default behavior is to overwrite any existing file.
|
||||
Call this with write_bytes(bytes, append=True) to append instead.
|
||||
"""
|
||||
if append:
|
||||
mode = 'ab'
|
||||
else:
|
||||
mode = 'wb'
|
||||
f = self.open(mode)
|
||||
try:
|
||||
f.write(bytes)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def read_file_text(self, encoding=None, errors='strict'):
|
||||
""" Open this file, read it in, return the content as a string.
|
||||
|
||||
This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
|
||||
are automatically translated to '\n'.
|
||||
|
||||
Optional arguments:
|
||||
|
||||
encoding - The Unicode encoding (or character set) of
|
||||
the file. If present, the content of the file is
|
||||
decoded and returned as a unicode object; otherwise
|
||||
it is returned as an 8-bit str.
|
||||
errors - How to handle Unicode errors; see help(str.decode)
|
||||
for the options. Default is 'strict'.
|
||||
"""
|
||||
if encoding is None:
|
||||
# 8-bit
|
||||
f = self.open(_textmode)
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
# Unicode
|
||||
f = codecs.open(self, 'r', encoding, errors)
|
||||
# (Note - Can't use 'U' mode here, since codecs.open
|
||||
# doesn't support 'U' mode, even in Python 2.3.)
|
||||
try:
|
||||
t = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
return (t.replace(u'\r\n', u'\n')
|
||||
.replace(u'\r\x85', u'\n')
|
||||
.replace(u'\r', u'\n')
|
||||
.replace(u'\x85', u'\n')
|
||||
.replace(u'\u2028', u'\n'))
|
||||
|
||||
def write_file_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
|
||||
""" Write the given text to this file.
|
||||
|
||||
The default behavior is to overwrite any existing file;
|
||||
to append instead, use the 'append=True' keyword argument.
|
||||
|
||||
There are two differences between path.write_file_text() and
|
||||
path.write_file_bytes(): newline handling and Unicode handling.
|
||||
See below.
|
||||
|
||||
Parameters:
|
||||
|
||||
- text - str/unicode - The text to be written.
|
||||
|
||||
- encoding - str - The Unicode encoding that will be used.
|
||||
This is ignored if 'text' isn't a Unicode string.
|
||||
|
||||
- errors - str - How to handle Unicode encoding errors.
|
||||
Default is 'strict'. See help(unicode.encode) for the
|
||||
options. This is ignored if 'text' isn't a Unicode
|
||||
string.
|
||||
|
||||
- linesep - keyword argument - str/unicode - The sequence of
|
||||
characters to be used to mark end-of-line. The default is
|
||||
os.linesep. You can also specify None; this means to
|
||||
leave all newlines as they are in 'text'.
|
||||
|
||||
- append - keyword argument - bool - Specifies what to do if
|
||||
the file already exists (True: append to the end of it;
|
||||
False: overwrite it.) The default is False.
|
||||
|
||||
|
||||
--- Newline handling.
|
||||
|
||||
write_text() converts all standard end-of-line sequences
|
||||
('\n', '\r', and '\r\n') to your platform's default end-of-line
|
||||
sequence (see os.linesep; on Windows, for example, the
|
||||
end-of-line marker is '\r\n').
|
||||
|
||||
If you don't like your platform's default, you can override it
|
||||
using the 'linesep=' keyword argument. If you specifically want
|
||||
write_text() to preserve the newlines as-is, use 'linesep=None'.
|
||||
|
||||
This applies to Unicode text the same as to 8-bit text, except
|
||||
there are three additional standard Unicode end-of-line sequences:
|
||||
u'\x85', u'\r\x85', and u'\u2028'.
|
||||
|
||||
(This is slightly different from when you open a file for
|
||||
writing with fopen(filename, "w") in C or file(filename, 'w')
|
||||
in Python.)
|
||||
|
||||
|
||||
--- Unicode
|
||||
|
||||
If 'text' isn't Unicode, then apart from newline handling, the
|
||||
bytes are written verbatim to the file. The 'encoding' and
|
||||
'errors' arguments are not used and must be omitted.
|
||||
|
||||
If 'text' is Unicode, it is first converted to bytes using the
|
||||
specified 'encoding' (or the default encoding if 'encoding'
|
||||
isn't specified). The 'errors' argument applies only to this
|
||||
conversion.
|
||||
|
||||
"""
|
||||
if isinstance(text, unicode):
|
||||
if linesep is not None:
|
||||
# Convert all standard end-of-line sequences to
|
||||
# ordinary newline characters.
|
||||
text = (text.replace(u'\r\n', u'\n')
|
||||
.replace(u'\r\x85', u'\n')
|
||||
.replace(u'\r', u'\n')
|
||||
.replace(u'\x85', u'\n')
|
||||
.replace(u'\u2028', u'\n'))
|
||||
text = text.replace(u'\n', linesep)
|
||||
if encoding is None:
|
||||
encoding = sys.getdefaultencoding()
|
||||
bytes = text.encode(encoding, errors)
|
||||
else:
|
||||
# It is an error to specify an encoding if 'text' is
|
||||
# an 8-bit string.
|
||||
assert encoding is None
|
||||
|
||||
if linesep is not None:
|
||||
text = (text.replace('\r\n', '\n')
|
||||
.replace('\r', '\n'))
|
||||
bytes = text.replace('\n', linesep)
|
||||
|
||||
self.write_file_bytes(bytes, append)
|
||||
|
||||
def read_file_lines(self, encoding=None, errors='strict', retain=True):
|
||||
""" Open this file, read all lines, return them in a list.
|
||||
|
||||
Optional arguments:
|
||||
encoding - The Unicode encoding (or character set) of
|
||||
the file. The default is None, meaning the content
|
||||
of the file is read as 8-bit characters and returned
|
||||
as a list of (non-Unicode) str objects.
|
||||
errors - How to handle Unicode errors; see help(str.decode)
|
||||
for the options. Default is 'strict'
|
||||
retain - If true, retain newline characters; but all newline
|
||||
character combinations ('\r', '\n', '\r\n') are
|
||||
translated to '\n'. If false, newline characters are
|
||||
stripped off. Default is True.
|
||||
|
||||
This uses 'U' mode in Python 2.3 and later.
|
||||
"""
|
||||
if encoding is None and retain:
|
||||
f = self.open(_textmode)
|
||||
try:
|
||||
return f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
return self.read_file_text(encoding, errors).splitlines(retain)
|
||||
|
||||
def write_file_lines(self, lines, encoding=None, errors='strict',
|
||||
linesep=os.linesep, append=False):
|
||||
""" Write the given lines of text to this file.
|
||||
|
||||
By default this overwrites any existing file at this path.
|
||||
|
||||
This puts a platform-specific newline sequence on every line.
|
||||
See 'linesep' below.
|
||||
|
||||
lines - A list of strings.
|
||||
|
||||
encoding - A Unicode encoding to use. This applies only if
|
||||
'lines' contains any Unicode strings.
|
||||
|
||||
errors - How to handle errors in Unicode encoding. This
|
||||
also applies only to Unicode strings.
|
||||
|
||||
linesep - The desired line-ending. This line-ending is
|
||||
applied to every line. If a line already has any
|
||||
standard line ending ('\r', '\n', '\r\n', u'\x85',
|
||||
u'\r\x85', u'\u2028'), that will be stripped off and
|
||||
this will be used instead. The default is os.linesep,
|
||||
which is platform-dependent ('\r\n' on Windows, '\n' on
|
||||
Unix, etc.) Specify None to write the lines as-is,
|
||||
like file.writelines().
|
||||
|
||||
Use the keyword argument append=True to append lines to the
|
||||
file. The default is to overwrite the file. Warning:
|
||||
When you use this with Unicode data, if the encoding of the
|
||||
existing data in the file is different from the encoding
|
||||
you specify with the encoding= parameter, the result is
|
||||
mixed-encoding data, which can really confuse someone trying
|
||||
to read the file later.
|
||||
"""
|
||||
if append:
|
||||
mode = 'ab'
|
||||
else:
|
||||
mode = 'wb'
|
||||
f = self.open(mode)
|
||||
try:
|
||||
for line in lines:
|
||||
isUnicode = isinstance(line, unicode)
|
||||
if linesep is not None:
|
||||
# Strip off any existing line-end and add the
|
||||
# specified linesep string.
|
||||
if isUnicode:
|
||||
if line[-2:] in (u'\r\n', u'\x0d\x85'):
|
||||
line = line[:-2]
|
||||
elif line[-1:] in (u'\r', u'\n',
|
||||
u'\x85', u'\u2028'):
|
||||
line = line[:-1]
|
||||
else:
|
||||
if line[-2:] == '\r\n':
|
||||
line = line[:-2]
|
||||
elif line[-1:] in ('\r', '\n'):
|
||||
line = line[:-1]
|
||||
line += linesep
|
||||
if isUnicode:
|
||||
if encoding is None:
|
||||
encoding = sys.getdefaultencoding()
|
||||
line = line.encode(encoding, errors)
|
||||
f.write(line)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
# --- Methods for querying the filesystem.
|
||||
|
||||
exists = os.path.exists
|
||||
isabs = os.path.isabs
|
||||
isdir = os.path.isdir
|
||||
isfile = os.path.isfile
|
||||
islink = os.path.islink
|
||||
ismount = os.path.ismount
|
||||
|
||||
if hasattr(os.path, 'samefile'):
|
||||
samefile = os.path.samefile
|
||||
|
||||
def atime(self):
|
||||
return os.path.getatime(self)
|
||||
|
||||
def mtime(self):
|
||||
return os.path.getmtime(self)
|
||||
|
||||
if hasattr(os.path, 'getctime'):
|
||||
def ctime(self):
|
||||
return os.path.getctime(self)
|
||||
|
||||
getsize = os.path.getsize
|
||||
|
||||
if hasattr(os, 'access'):
|
||||
def access(self, mode):
|
||||
""" Return true if current user has access to this path.
|
||||
|
||||
mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
|
||||
"""
|
||||
return os.access(self, mode)
|
||||
|
||||
def stat(self):
|
||||
""" Perform a stat() system call on this path. """
|
||||
return os.stat(self)
|
||||
|
||||
def lstat(self):
|
||||
""" Like path.stat(), but do not follow symbolic links. """
|
||||
return os.lstat(self)
|
||||
|
||||
if hasattr(os, 'statvfs'):
|
||||
def statvfs(self):
|
||||
""" Perform a statvfs() system call on this path. """
|
||||
return os.statvfs(self)
|
||||
|
||||
if hasattr(os, 'pathconf'):
|
||||
def pathconf(self, name):
|
||||
return os.pathconf(self, name)
|
||||
|
||||
# --- Modifying operations on files and directories
|
||||
|
||||
def utime(self, times):
|
||||
""" Set the access and modified times of this file. """
|
||||
os.utime(self, times)
|
||||
|
||||
def chmod(self, mode):
|
||||
os.chmod(self, mode)
|
||||
|
||||
if hasattr(os, 'chown'):
|
||||
def chown(self, uid, gid):
|
||||
os.chown(self, uid, gid)
|
||||
|
||||
def rename(self, new):
|
||||
os.rename(self, new)
|
||||
|
||||
def renames(self, new):
|
||||
os.renames(self, new)
|
||||
|
||||
|
||||
# --- Create/delete operations on directories
|
||||
|
||||
def mkdir(self, mode=0777):
|
||||
os.mkdir(self, mode)
|
||||
|
||||
def makedirs(self, mode=0777):
|
||||
os.makedirs(self, mode)
|
||||
|
||||
def rmdir(self):
|
||||
os.rmdir(self)
|
||||
|
||||
def removedirs(self):
|
||||
os.removedirs(self)
|
||||
|
||||
|
||||
# --- Modifying operations on files
|
||||
|
||||
def touch(self, mode=None):
|
||||
""" Set the access/modified times of this file to the current time.
|
||||
Create the file if it does not exist.
|
||||
|
||||
The file mode is only set if the file must be created.
|
||||
The mode argument can be None, in which case the current umask is used.
|
||||
"""
|
||||
if mode is None:
|
||||
mode = os.umask(0)
|
||||
os.umask(mode)
|
||||
mode = mode ^ 0777
|
||||
fd = os.open(self, os.O_WRONLY | os.O_CREAT, mode)
|
||||
os.close(fd)
|
||||
self.utime(None)
|
||||
|
||||
def remove(self):
|
||||
os.remove(self)
|
||||
|
||||
def unlink(self):
|
||||
os.unlink(self)
|
||||
|
||||
|
||||
# --- Links
|
||||
|
||||
if hasattr(os, 'link'):
|
||||
def link(self, newpath):
|
||||
""" Create a hard link at 'newpath', pointing to this file. """
|
||||
os.link(self, newpath)
|
||||
|
||||
if hasattr(os, 'symlink'):
|
||||
def symlink(self, newlink):
|
||||
""" Create a symbolic link at 'newlink', pointing here. """
|
||||
os.symlink(self, newlink)
|
||||
|
||||
if hasattr(os, 'readlink'):
|
||||
def readlink(self):
|
||||
""" Return the path to which this symbolic link points.
|
||||
|
||||
The result may be an absolute or a relative path.
|
||||
"""
|
||||
return self.__class__(os.readlink(self))
|
||||
|
||||
def readlinkabs(self):
|
||||
""" Return the path to which this symbolic link points.
|
||||
|
||||
The result is always an absolute path.
|
||||
"""
|
||||
p = self.readlink()
|
||||
if p.isabs():
|
||||
return p
|
||||
else:
|
||||
return (self.directory / p).abspath()
|
||||
|
||||
# --- High-level functions from shutil
|
||||
|
||||
copyfile = shutil.copyfile
|
||||
copymode = shutil.copymode
|
||||
copystat = shutil.copystat
|
||||
copy = shutil.copy
|
||||
copy2 = shutil.copy2
|
||||
copytree = shutil.copytree
|
||||
if hasattr(shutil, 'move'):
|
||||
move = shutil.move
|
||||
rmtree = shutil.rmtree
|
||||
|
||||
# --- Special stuff from os
|
||||
|
||||
if hasattr(os, 'chroot'):
|
||||
def chroot(self):
|
||||
os.chroot(self)
|
||||
|
||||
if hasattr(os, 'startfile'):
|
||||
def startfile(self):
|
||||
os.startfile(self)
|
||||
|
||||
if hasattr(os, 'chdir'):
|
||||
def chdir(self):
|
||||
os.chdir(self)
|
||||
11
tools/rehelper.py
Normal file
11
tools/rehelper.py
Normal file
@@ -0,0 +1,11 @@
|
||||
def identifier():
|
||||
return "[a-zA-Z_][0-9a-zA-Z_]*"
|
||||
|
||||
def wholeWord(x):
|
||||
return "\\b%s\\b"%x
|
||||
|
||||
def group(x, name=None):
|
||||
if name:
|
||||
return "(?P<%s>%s)"%(name, x)
|
||||
else:
|
||||
return "(%s)"%x
|
||||
182
tools/xperia_check.py
Normal file
182
tools/xperia_check.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# tools for making sure nothing is wrongly set
|
||||
|
||||
from xml.dom.minidom import parse, parseString
|
||||
|
||||
AName = "android:name"
|
||||
|
||||
def hasPermissions(Perms):
|
||||
def _hasPermissions(root):
|
||||
perms = Perms[:]
|
||||
elems = root.getElementsByTagName("uses-permission")
|
||||
failed = []
|
||||
for e in elems:
|
||||
if e.hasAttribute(AName):
|
||||
attr = e.getAttribute(AName)
|
||||
try: perms.remove(attr)
|
||||
except: failed.append(attr)
|
||||
success = len(perms) == 0
|
||||
if not success:
|
||||
print "Permissions failed:\n\t%s"%",\n\t".join(perms)
|
||||
return success
|
||||
return _hasPermissions
|
||||
|
||||
def testXperiaOptimized(root):
|
||||
elem = root.getElementsByTagName("application")[0]
|
||||
metas = elem.getElementsByTagName("meta-data")
|
||||
for x in metas:
|
||||
if x.hasAttribute(AName):
|
||||
if x.getAttribute(AName) == "xperiaplayoptimized_content":
|
||||
return True
|
||||
return False
|
||||
|
||||
def testManifestHeader(root):
|
||||
package = root.getAttribute("package")
|
||||
verCode = root.getAttribute("android:versionCode")
|
||||
verName = root.getAttribute("android:versionName")
|
||||
|
||||
print "\n - Package : %s"%package
|
||||
print " - Version Code: %s"%verCode
|
||||
print " - Version Name: %s"%verName
|
||||
|
||||
try:
|
||||
if root.getAttribute("android:installLocation") != "preferExternal":
|
||||
raise Error
|
||||
except: print "Warning: android:installLocation is not 'preferExternal"
|
||||
|
||||
return True
|
||||
|
||||
def testXperiaOnly(root):
|
||||
elem = root.getElementsByTagName("application")[0]
|
||||
libs = elem.getElementsByTagName("uses-library")
|
||||
for x in libs:
|
||||
if x.getAttribute(AName) == "xperiaplaycertified" and \
|
||||
x.getAttribute("android:required") == "true":
|
||||
return True
|
||||
return False
|
||||
|
||||
def testPlaystationOnly(root):
|
||||
elem = root.getElementsByTagName("application")[0]
|
||||
libs = elem.getElementsByTagName("uses-library")
|
||||
for x in libs:
|
||||
if x.getAttribute(AName) == "playstationcertified" and \
|
||||
x.getAttribute("android:required") == "true":
|
||||
return True
|
||||
return False
|
||||
|
||||
def testNameAndIcon(root):
|
||||
elem = root.getElementsByTagName("application")[0]
|
||||
metas = elem.getElementsByTagName("meta-data")
|
||||
hasName = hasIcon = False
|
||||
for x in metas:
|
||||
if x.hasAttribute(AName):
|
||||
if x.getAttribute(AName) == "game_display_name": hasName = True
|
||||
if x.getAttribute(AName) == "game_icon": hasIcon = True
|
||||
return hasName and hasIcon
|
||||
|
||||
def testDebuggable(root):
|
||||
elem = root.getElementsByTagName("application")[0]
|
||||
if not elem.hasAttribute("android:debuggable"): return True
|
||||
return elem.getAttribute("android:debuggable") == "false"
|
||||
|
||||
def testMinSdkVersion(Version):
|
||||
def _minSdkVersion(root):
|
||||
elem = root.getElementsByTagName("uses-sdk")[0]
|
||||
if not elem.hasAttribute("android:minSdkVersion"): return False
|
||||
return int(elem.getAttribute("android:minSdkVersion")) == Version
|
||||
return _minSdkVersion
|
||||
|
||||
def testMetaData(root):
|
||||
success = True
|
||||
success &= testXperiaOptimized(root)
|
||||
success &= testNameAndIcon(root)
|
||||
return success
|
||||
|
||||
def testRealLicenseCheck(root):
|
||||
import os
|
||||
p, mfn = os.path.split(fn)
|
||||
srcfn = os.path.join(p, "src/com/mojang/minecraftpe/MainActivity.java")
|
||||
s = file(srcfn, 'r').read()
|
||||
x = s.find("new VerizonLicenseThread")
|
||||
if x < 0: return False
|
||||
|
||||
b = s.find("(", x)
|
||||
e = s.find(")", b)
|
||||
if -1 in (b,e): return False
|
||||
args = [arg.strip() for arg in s[b+1:e].split(",")]
|
||||
if len(args) != 3:
|
||||
print "Couldn't parse new VerizonLicenseThread() arguments"
|
||||
return False
|
||||
return args[2] == "false"
|
||||
|
||||
def verify(doc, rules):
|
||||
success = True
|
||||
root = doc.documentElement
|
||||
for rule in rules:
|
||||
print ".",
|
||||
try:
|
||||
ruleResult = rule(root)
|
||||
except:
|
||||
ruleResult = False
|
||||
import traceback
|
||||
print traceback.format_exc()
|
||||
if not ruleResult:
|
||||
print " Rule %s failed."%rule
|
||||
success = False
|
||||
return success
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
generalPerms = hasPermissions([ "android.permission.INTERNET",
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE"])
|
||||
verizonPerms = hasPermissions([ "android.permission.READ_PHONE_STATE",
|
||||
"android.permission.START_BACKGROUND_SERVICE",
|
||||
"com.verizon.vcast.apps.VCAST_APPS_LICENSE_SERVICE"])
|
||||
marketPerms = hasPermissions([ "com.android.vending.CHECK_LICENSE"])
|
||||
|
||||
Google = "google"
|
||||
Exclusive = "exclusive"
|
||||
Verizon = "verizon"
|
||||
General = "general"
|
||||
allStores = {
|
||||
General:[ generalPerms,
|
||||
testXperiaOptimized,
|
||||
testDebuggable,
|
||||
testMinSdkVersion(9),
|
||||
testNameAndIcon,
|
||||
testManifestHeader],
|
||||
Google: [ marketPerms],
|
||||
Verizon:[ verizonPerms,
|
||||
testRealLicenseCheck],
|
||||
Exclusive: [testXperiaOnly,
|
||||
testPlaystationOnly]
|
||||
}
|
||||
|
||||
try: fn = sys.argv[1]
|
||||
except: fn = r'C:\dev\subversion\mojang\minecraftcpp\trunk\handheld\project\android\AndroidManifest.xml'
|
||||
|
||||
try: stores = sys.argv[2].lower().split(",")
|
||||
except: stores = allStores.keys()
|
||||
if not General in stores: stores.append(General)
|
||||
|
||||
print "Manifest file: %s\n"%fn
|
||||
|
||||
dom = parse(fn)
|
||||
root = dom.documentElement
|
||||
#print root
|
||||
|
||||
for s in stores:
|
||||
if s not in allStores:
|
||||
print "Code '%s' not found"%s
|
||||
continue
|
||||
print "Running through '%s'"%s
|
||||
rules = allStores[s]
|
||||
if verify(dom, rules):
|
||||
print "Success"
|
||||
print "\n"
|
||||
|
||||
print """
|
||||
* Raise version number (versionName and versionCode)
|
||||
* Sign the application with same key as always
|
||||
* Run zip-align on the signed apk
|
||||
"""
|
||||
Reference in New Issue
Block a user