#!/usr/bin/python
import sys, os, struct, array, time
HEXFILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in xrange(256)])
def getbyte(d):
return struct.unpack("<B",d)[0]
def getword(d):
return struct.unpack("<H",d)[0]
def getdword(d):
return struct.unpack("<I",d)[0]
def dump(src, length=16):
N=0; result=''
while src:
s,src = src[:length],src[length:]
hexa = ' '.join(["%02X"%ord(x) for x in s])
s = s.translate(HEXFILTER)
result += "%04X: %-*s %s\n" % (N, length*3, hexa, s)
N+=length
return result
VERSION = "0.95"
class BlockDevice:
def __init__(self, file):
self.fd = open(file,"rb+")
self.written = {}
self.sectorsize = 512
def read(self, sector):
if sector in self.written:
return self.written[sector]
else:
self.fd.seek(sector*self.sectorsize)
data = self.fd.read(self.sectorsize)
return data
def mread(self, start, length):
self.fd.seek(start*self.sectorsize)
data = self.fd.read(self.sectorsize*length)
for i in xrange(length):
if (i+start) in self.written:
data = data[:i*self.sectorsize] + self.written[i+start] + data[i*self.sectorsize+self.sectorsize:]
return data
def write(self, sector, data):
if len(data) != self.sectorsize:
raise ValueError("Data size must equal sector size (%d, should be %d)"%(data,self.sectorsize))
old = self.read(sector)
if old == data:
return
self.written[sector] = data
def commit(self):
count = 0
for sector,data in self.written.items():
self.fd.seek(sector*self.sectorsize)
self.fd.write(data)
count += 1
self.written = {}
return count
def size(self):
self.fd.seek(0,2)
size = self.fd.tell()
if size%self.sectorsize != 0:
raise RuntimeError("Device size not divisible by sector size!")
return self.fd.tell()/self.sectorsize
def revert(self):
self.written = {}
def dumpchanges(self):
items = self.written.items()
items.sort(lambda x,y: cmp(x[0],y[0]))
for sector,data in items:
print "== SECTOR 0x%08X ==========================================================================="%sector
print dump(data)
class Partition:
def __init__(self, blockdevice, start, length, name, description=""):
self.blockdevice = blockdevice
self.start = start
self.length = length
self.name = name
self.description = description
def read(self, sector):
if sector >= self.length:
raise ValueError("Attempted to read data beyond partition end")
else:
return self.blockdevice.read(sector+self.start)
def mread(self, sector, length):
if (sector+length) > self.length:
raise ValueError("Attempted to read data beyond partition end")
else:
# d = ""
return self.blockdevice.mread(sector+self.start,length)
# for i in xrange(length):
# d += self.blockdevice.read(sector+self.start+i)
# return d
def write(self, sector, data):
if sector >= self.length:
raise ValueError("Attempted to write data beyond partition end")
else:
self.blockdevice.write(sector+self.start, data)
def mwrite(self, sector, data):
if len(data)%self.blockdevice.sectorsize != 0:
raise ValueError("Data size must be a multiple of the sector size")
else:
dsecs = len(data)/self.blockdevice.sectorsize
if (sector+dsecs) > self.length:
raise ValueError("Attempted to write data beyond partition end")
else:
for i in xrange(dsecs):
self.blockdevice.write(sector+i+self.start, data[i*self.blockdevice.sectorsize:(i+1)*self.blockdevice.sectorsize])
def __str__(self):
return "[%s] %d - %d (%d sectors, %.1fMB): %s"%(self.name,self.start,self.start+self.length-1,self.length,self.length/2048.0,self.description)
class PartitionCollection:
def __init__(self, blockdevice):
self.blockdevice = blockdevice
self.managers = []
self.partitions = []
def register(self, partitionmanager):
self.managers.append(partitionmanager)
self.partitions += partitionmanager.gettable()
def refresh(self):
self.partitions = []
for i in self.managers:
self.partitions += i.gettable()
def __getitem__(self, item):
return self.partitions.__getitem__(item)
def __len__(self):
return self.partitions.__len__()
def __str__(self):
s = "Partition Table:\n"
for i,part in enumerate(self.partitions):
s += " %d: %s\n"%(i+1,str(part))
return s
class MBRPartitionTable:
def __init__(self, device, prefix="MBR-", sector=0):
self.sector = sector
self.prefix = prefix
self.device = device
def readentry(self, num):
if num >= 4:
raise ValueError("MBR Partition index out of bounds")
offset = num * 16 + 0x1be
entry = self.device.read(self.sector)[offset:offset+16]
boot,sh,scs1,scs2,ptype,eh,ecs1,ecs2,start,size = struct.unpack("<BBBBBBBBII",entry)
schs = (scs2 + ((scs1 & 0xc0) << 2),sh,scs1&0x3F)
echs = (ecs2 + ((ecs1 & 0xc0) << 2),eh,ecs1&0x3F)
return (boot&0x80==0x80),ptype,start,size,schs,echs
def writeentry(self, num, boot, ptype, start, size):
if num >= 4:
raise ValueError("MBR Partition index out of bounds")
offset = num * 16 + 0x1be
if boot:
bent = 0x80
else:
bent = 0x00
entry = struct.pack("<BBBBBBBBII",bent,0xff,0xff,0xff,ptype,0xff,0xff,0xff,start,size)
sector = self.device.read(self.sector)
sector = sector[:offset] + entry + sector[offset+16:]
self.device.write(self.sector,sector)
def hasentry(self, num):
if num >= 4:
raise ValueError("MBR Partition index out of bounds")
offset = num * 16 + 0x1be
entry = self.device.read(self.sector)[offset:offset+16]
boot,sh,scs1,scs2,ptype,eh,ecs1,ecs2,start,size = struct.unpack("<BBBBBBBBII",entry)
return ptype != 0x00
def gettable(self):
table = []
for i in xrange(4):
bootable,ptype,start,size,schs,echs = self.readentry(i)
if ptype != 0x00:
table.append(Partition(self.device,start,size,self.prefix+"%d"%(i+1),"MBR Partition of type 0x%02x"%ptype))
return table
class NonFatalFilesystemError(RuntimeError):
pass
class XBOXPartitionTable:
def __init__(self, device, useFG=False, prefix="XBOX-"):
self.prefix = prefix
self.device = device
self.useFG = useFG
self.devicesize = self.device.size()
if self.devicesize < 0x00EE8AB0:
raise RuntimeError("Device too small to be an XBOX Hard Disk: %d sectors"%self.devicesizee)
sector = self.device.read(3)
sig = sector[0:4]
print dump(sig)
if sig!=0x42524652:
print "This XBOX Hard Disk signature is missing."
yesno = raw_input("Do you want to mark it? (Yes/No) (You need to type 'Yes'): ")
if yesno == "Yes":
sig2 = struct.pack("<4s","BRFR")
sector = sig2 + sector[4:]
print dump(sector)
offset = 3
self.device.write(offset,sector)
self.device.commit
else:
raise RuntimeError("Invalid XBOX Config Area signature: '%s'"%repr(sig))
def gettable(self):
table = []
table.append(Partition(self.device,0x00000000,0x00000003-0x00000000,self.prefix+"BOOT","XBOX Legacy Boot Area (MBR)"))
table.append(Partition(self.device,0x00000003,0x00000400-0x00000003,self.prefix+"CONFIG","XBOX Configuration Area"))
table.append(Partition(self.device,0x00000400,0x00177400-0x00000400,self.prefix+"X","XBOX Game Cache 1"))
table.append(Partition(self.device,0x00177400,0x002EE400-0x00177400,self.prefix+"Y","XBOX Game Cache 2"))
table.append(Partition(self.device,0x002EE400,0x00465400-0x002EE400,self.prefix+"Z","XBOX Game Cache 3"))
table.append(Partition(self.device,0x00465400,0x0055F400-0x00465400,self.prefix+"C","XBOX System"))
table.append(Partition(self.device,0x0055F400,0x00EE8AB0-0x0055F400,self.prefix+"E","XBOX Data"))
if self.devicesize > 0x00EE8AB0:
if self.devicesize <= 0x10000000:
table.append(Partition(self.device,0x00EE8AB0,self.devicesize-0x00EE8AB0,self.prefix+"F","XBOX Extended"))
else:
if self.useFG:
table.append(Partition(self.device,0x00EE8AB0,0x10000000-0x00EE8AB0,self.prefix+"F","XBOX Extended"))
table.append(Partition(self.device,0x10000000,self.devicesize-0x10000000,self.prefix+"G","XBOX LBA48 Extended"))
else:
table.append(Partition(self.device,0x00EE8AB0,self.devicesize-0x00EE8AB0,self.prefix+"F","XBOX Extended"))
return table
class Filesystem:
def __init__(self, partition):
self.partition = partition
class FATX(Filesystem):
def __init__(self, partition):
Filesystem.__init__(self, partition)
self.bootblock = partition.read(0)
self.justFormatted = False
print dump(self.bootblock[0:4])
if self.bootblock[0:4] != 0x46415458:
print "This FATX filesystem is not formatted."
yesno = raw_input("Do you want to format it? (Yes/No) (You need to type 'Yes'): ")
if yesno == "Yes":
print "Writing header..."
import random
size = self.partition.length*self.partition.blockdevice.sectorsize
clustersize=0x20
self.bootblock = struct.pack("<4sIIHI","FATX",random.randint(0,2**32-1),clustersize,1,0)
self.bootblock += "\xff"*0xFEE
self.partition.mwrite(0,self.bootblock)
print "Writing FAT..."
self.readfsinfo()
self.showinfo()
self.fat = array.array(self.arraycode,((self.dataclusters+1))*[self.fat_unused])
self.fat[0] = self.fat_eoc
self.fat[1] = self.fat_eoc_set # let's do things the MS way...
fatstring = self.fat.tostring()
fatstring += ((self.fatsize*512)-len(fatstring))*"\xff"
self.partition.mwrite(8,fatstring)
print "Writing Root Directory..."
self.putcluster(self.rootdir,"\xff"*self.clustersize*512)
print "Done!"
self.justFormatted = True
return
else:
raise NonFatalFilesystemError("Invalid FATX signature")
self.readfsinfo()
def readfsinfo(self):
self.volumeid, self.clustersize, self.fatcopies = struct.unpack("IIH",self.bootblock[4:14])
if self.fatcopies != 1:
raise NonFatalFilesystemError("FATX has %d FAT copies, only 1 supported"%self.fatcopies)
self.numclusters = self.partition.length / self.clustersize
if self.numclusters >= 0xfff4:
self.fatbits = 32
self.arraycode = "I"
self.fat_unused = 0x00000000L
self.fat_eoc = 0xfffffff8L
self.fat_eoc_set = 0xffffffffL
self.fat_reserved = 0xfffffff0L
self.fat_bad = 0xfffffff7L
else:
self.fatbits = 16
self.fat_unused = 0x0000
self.fat_eoc = 0xfff8
self.fat_eoc_set = 0xffff
self.fat_reserved = 0xfff0
self.fat_bad = 0xfff7
self.arraycode = "H"
self.fatsize = self.numclusters * self.fatbits/8
if self.fatsize % 4096 != 0:
self.fatsize = ((self.fatsize / 4096) + 1) * 4096
self.fatsize /= 512
self.dataoffset = self.fatsize + 8 - self.clustersize
self.rootdir = 1
self.dataclusters = (self.partition.length - self.fatsize - 8)/self.clustersize
#print dump(self.partition.mread(8,2))
def getcluster(self, num):
if num < 1:
raise ValueError("Invalid FAT cluster number %d"%(num))
return self.partition.mread(self.dataoffset+(num)*self.clustersize, self.clustersize)
def putcluster(self, num, data):
if num < 1:
raise ValueError("Invalid FAT cluster number %d"%(num))
if len(data) != 512*self.clustersize:
raise ValueError("Invalid FAT cluster size %d"%(num))
self.partition.mwrite(self.dataoffset+(num)*self.clustersize, data)
def showinfo(self):
print " FATX volume on %s: %.1fMB raw size"%(self.partition.name,self.partition.length/2048.0)
print " Volume ID: %08X"%self.volumeid
print " Cluster size: %d sectors (%dKB)"%(self.clustersize,self.clustersize/2)
print " FAT type: %d bits"%(self.fatbits)
print " FAT copies: %d"%(self.fatcopies)
print " FAT size: %d sectors (%dKB)"%(self.fatsize,self.fatsize/2)
print " Total clusters: %d (%d data clusters)"%(self.numclusters,self.dataclusters)
def readchain(self, cluster):
data = ""
while cluster < self.fat_eoc:
#print "Reading chain, cluster = %d"%cluster
d = self.getcluster(cluster)
data += d
#print "Cluster length: %d"%len(d)
cluster = self.fat[cluster]
#print "Chain length: %d"%len(data)
return data
def writechain(self, cluster, data):
while cluster < self.fat_eoc:
self.putcluster(cluster, data[:self.clustersize*512])
data = data[self.clustersize*512:]
cluster = self.fat[cluster]
if data != "":
raise RuntimeError("Chain length mismatch when writing chain!")
def extendchain(self, cluster):
#print "Extending cluster chain %d..."%cluster
if cluster == self.rootdir:
raise RuntimeError("Root directory is full and cannot be extended! (Or so I hear, it may very well be possible)")
while self.fat[cluster] < self.fat_eoc:
cluster = self.fat[cluster]
#print "- Start %d"%cluster
for newcluster in xrange(len(self.fat)):
if self.fat[newcluster] == self.fat_unused:
print "- New %d"%newcluster
self.fat[newcluster] = self.fat_eoc_set
self.fat[cluster] = newcluster
return
else:
raise RuntimeError("No free clusters available")
def finddir(self, path):
cd = self.rootdir
path = path.replace("\\","/")
for element in path.split("/"):
if element == "":
continue
dirdata = self.readchain(cd)
for i in xrange(len(dirdata)/64):
namesz,attr,name,cluster,size,mtime,ctime,atime = struct.unpack("<BB42sIIIII",dirdata[i*64:i*64+64])
if namesz == 0xff or namesz == 0x00:
return RuntimeError("Path element %s not found"%element)
if namesz > 42:
continue
name = name[:namesz]
if name.lower() == element.lower():
cd = cluster
break
else:
raise RuntimeError("Path element %s not found"%element)
return cd
def addfile(self, dircluster, filename, start, length):
dirdata = self.readchain(dircluster)
#print "SDir: %d"%len(dirdata)
dirdataextension = 0
if len(filename) > 42:
raise RuntimeError("Filename length exceeds maximum of 42: %s (%d)"%(filename,len(filename)))
pfilename = filename+"\xFF"*(42-len(filename))
# first check for existing file
for i in xrange(len(dirdata)/64):
namesz,attr,name,cluster,size,mtime,ctime,atime = struct.unpack("<BB42sIIIII",dirdata[i*64:i*64+64])
if namesz == 0xff or namesz == 0:
break
if namesz > 42:
continue
name = name[:namesz]
if name.lower() == filename.lower():
raise RuntimeError("File %s already exists!"%filename)
# Now store the file
for i in xrange(len(dirdata)/64):
namesz,attr,name,cluster,size,mtime,ctime,atime = struct.unpack("<BB42sIIIII",dirdata[i*64:i*64+64])
if namesz == 0xff or namesz == 0x00:
dirdata = dirdata[:i*64] + struct.pack("<BB42sIIIII",len(filename),0,pfilename,start,length,0,0,0) + dirdata[i*64+64:]
#print "A Dir: %d"%len(dirdata)
if i == ((len(dirdata)/64) - 1):
dirdata += "\xFF"*(self.clustersize * 512)
dirdataextension += 1
#print "End-extending dir: %d %d"%(len(dirdata),dirdataextension)
dirdata = dirdata[:i*64+64] + 64*"\xFF" + dirdata[i*64+128:]
#print "B Dir: %d"%len(dirdata)
break
if namesz > 42:
dirdata = dirdata[:i*64] + struct.pack("<BB42sIIIII",len(filename),0,pfilename,start,length,0,0,0) + dirdata[i*64+64:]
break
else:
dirdataextension += 1
dirdata = dirdata + struct.pack("<BB42sIIIII",len(filename),0,pfilename,start,length,0,0,0) + \
"\xFF"*(self.clustersize * 512 - 64)
#print "Extending dir: %d %d"%(len(dirdata),dirdataextension)
for i in xrange(dirdataextension):
self.extendchain(dircluster)
#print "EDir: %d"%len(dirdata)
self.writechain(dircluster,dirdata)
def reserve_area(self):
print "Reading FAT...",
sys.stdout.flush()
start = 8
sleft = self.fatsize - 8
left = self.fatsize - 8
np = 0.1
fstr = ""
self.fat = array.array(self.arraycode)
while left > 0:
qty = min(left,1024)
s = self.partition.mread(start,qty)
self.fat.fromstring(s)
if self.fatsize > 20000 and np < (1-(float(left)/sleft)):
print ("%d%%"%int((np+0.001)*100)),
sys.stdout.flush()
np += 0.1
start += qty
left -= qty
print "Done."
print "Analyzing free space...",
sys.stdout.flush()
self.freegroups = []
self.totalfree = 0
lastfree = False
freestart = 0
np = 0.1
for cluster,value in enumerate(self.fat):
if cluster % 4096 == 0:
if np < cluster/float(self.numclusters):
print ("%d%%"%int((np+0.001)*100)),
sys.stdout.flush()
np += 0.1
if value == self.fat_unused:
self.totalfree += 1
if not lastfree:
freestart = cluster
lastfree = True
elif lastfree:
self.freegroups.append((freestart, cluster-freestart))
lastfree = False
if lastfree:
self.freegroups.append((freestart, cluster+1-freestart))
print "Done."
print "%d clusters free (%.1fMB)"%(self.totalfree, (self.totalfree*self.clustersize)/2048.0)
if self.totalfree == 0:
print "No space available!"
return False
self.freegroups.sort(lambda x,y: cmp(x[1],y[1]))
maxblock = self.freegroups[-1][1]
print "Largest contiguous block available: %d clusters (%.1fMB)"%(maxblock,maxblock*self.clustersize/2048.0)
havevalue = False
while True:
if not havevalue:
value = raw_input("Target partition size (#{K,M,G}): ")
havevalue = False
mult = 1
if not value:
continue
if value[-1] == "B":
value = value[:-1]
if not value:
print "Invalid size value."
continue
if value[-1] in "KMG":
if value[-1] == "K":
mult = 1024
elif value[-1] == "M":
mult = 1024*1024
elif value[-1] == "G":
mult = 1024*1024*1024
value=value[:-1]
if not value:
print "Invalid size value."
continue
try:
numbytes = int(value)
except ValueError:
print "Invalid size value."
continue
numbytes *= mult
if numbytes % 512 != 0:
numsectors = numbytes / 512 + 1
numbytes = numsectors * 512
else:
numsectors = numbytes / 512
if numsectors % self.clustersize != 0:
numclusters = numsectors / self.clustersize + 1
else:
numclusters = numsectors / self.clustersize
if numclusters > maxblock:
print "Size is too large"
continue
print "You requested %d sectors (%.1fMB)"%(numsectors, numsectors/2048.0)
nextlarger = len(self.freegroups)-1
nextsmaller = 0
if numbytes < self.freegroups[nextsmaller][1]*self.clustersize*512:
nextsmaller = None
else:
while numbytes >= self.freegroups[nextsmaller][1]*self.clustersize*512:
nextsmaller += 1
nextsmaller -= 1
while numbytes <= self.freegroups[nextlarger][1]*self.clustersize*512:
nextlarger -= 1
if nextlarger == -1:
break
nextlarger += 1
print "Closest contiguous block larger than the requested size: %d clusters (%.1fMB)"%\
(self.freegroups[nextlarger][1],self.freegroups[nextlarger][1]*self.clustersize/2048.0)
if nextsmaller is not None:
print "Closest contiguous block smaller than the requested size: %d clusters (%.1fMB)"%\
(self.freegroups[nextsmaller][1],self.freegroups[nextsmaller][1]*self.clustersize/2048.0)
print "- Enter L to use the size of the closest larger contiguous block"
if nextsmaller is not None:
print "- Enter S to use the size of the closest smaller contiguous block"
print "- Enter T to use the specified target size"
if numsectors % self.clustersize != 0:
print "- Enter R to use the specified target size, rounded to the next cluster (this is free)"
value = raw_input("- Or enter a new target size value (#{K,M,G}): ")
if value in "lL":
block = self.freegroups[nextlarger]
elif value in "sS":
block = self.freegroups[nextsmaller]
elif value in "tTrR":
block = self.freegroups[nextlarger][0],numclusters
if value in "rR":
numsectors = numclusters * self.clustersize
else:
havevalue = True
continue
if (self.totalfree-block[1])*self.clustersize < 2048:
print
print "You should probably leave some free space. Who knows what will happen otherwise."
print "Try again."
continue
kpercluster = self.clustersize / 2
allocbytes = block[1] * self.clustersize * 512
if numbytes >= 2**31-1:
print
print "The requested size will not fit into a standard file in FATX."
print "Please choose among the following options:"
print " A) Use multiple 1GB files. This is safe."
print " B) Use multiple 2GB-%dK files. This is safe, but not very neat."%kpercluster
print " C) Use multiple 2GB files. This will show up as -2GB using the MS kernel."
print " D) Use multiple 4GB-%dK files. This will show up as -%dK using the MS kernel."%(kpercluster,kpercluster)
print " E) Choose a new partition size."
choice = raw_input("Your choice: ")
while choice not in "ABCDEabcde":
print "Invalid choice %s"%choice
choice = raw_input("Your choice: ")
if choice in "eE":
continue
elif choice in "aA":
chunksize = 2048*1024/self.clustersize
elif choice in "bB":
chunksize = (4096*1024/self.clustersize)-1
elif choice in "cC":
chunksize = 4096*1024/self.clustersize
elif choice in "dD":
chunksize = (8096*1024/self.clustersize)-1
chunksrequired = numbytes / (chunksize*self.clustersize*512)
if numbytes % (chunksize*self.clustersize*512) != 0:
chunksrequired += 1
print
print "%d file(s) will be created."%chunksrequired
filetab = []
for filenum in xrange(chunksrequired):
start = filenum*chunksize + block[0]
length = min(chunksize, block[1]-filenum*chunksize)
bytelength = min(chunksize*self.clustersize,numsectors-(filenum*chunksize*self.clustersize))*512
print "Allocating %d clusters (%d - %d) for file %d (%.1fMB)..."%\
(length, start, start+length-1, filenum, length*self.clustersize/2048.0)
filetab.append((start, bytelength))
for cluster in xrange(start, start+length):
if cluster == (start+length-1):
self.fat[cluster] = self.fat_eoc_set
else:
self.fat[cluster] = cluster+1
print
path = raw_input("Enter the path where the files will be stored: ")
dircluster = self.finddir(path)
base = raw_input("Enter the basename for the files: ")
print "Writing %d files..."%chunksrequired
for file,entry in enumerate(filetab):
filename = base + ".%03d"%file
print " %s"%filename
start, length = entry
self.addfile(dircluster,filename,start,length)
else:
print "Allocating %d clusters (%d - %d, %.1fMB)..."%(numclusters, block[0],block[0]+block[1]-1,allocbytes/1024.0/1024.0)
for cluster in xrange(block[0], block[0]+block[1]):
if cluster == (block[0]+block[1]):
self.fat[cluster] = self.fat_eoc_set
else:
self.fat[cluster] = cluster+1
print
path = raw_input("Enter the path where the file will be stored: ")
dircluster = self.finddir(path)
filename = raw_input("Enter the filename: ")
print "Writing file %s..."%filename
self.addfile(dircluster,filename,block[0],numbytes)
print "Storing FAT back..."
fatstring = self.fat.tostring()
self.fat = None
fatstring += ((self.fatsize*512)-len(fatstring))*"\xff"
self.partition.mwrite(8,fatstring)
print "Done!"
return (block[0]*self.clustersize+self.dataoffset, block[1]*self.clustersize)
def main(argv):
print "subfdisk "+VERSION+" by Hector Martin <hector@marcansoft.com>"
if len(argv) < 2 or len(argv) > 3:
print "Usage: python %s [-g] <device>"%argv[0]
print "<device> should be the entire hard disk device, not a partition."
print "-g: use split F/G partitions for HDDs larger than 128GiB/137GB"
return
useFG = False
if argv[1] == "-g":
useFG = True
argv=argv[0:1]+argv[2:]
print
device = BlockDevice(argv[1])
print "%s: %d bytes (%.1fMB)"%(argv[1],device.size()*device.sectorsize,device.size()*device.sectorsize/1024.0/1024.0)
print
print "Reading partitions..."
while True:
partitions = PartitionCollection(device)
partitions.register(XBOXPartitionTable(device,useFG))
partitions.register(MBRPartitionTable(device))
print str(partitions)
while True:
part = raw_input("Choose the partition you want to work with: ")
if not part: continue
if part == "q": return
try:
numpart = int(part)-1
except ValueError:
for numpart,partition in enumerate(partitions):
if partition.name == part:
break
else:
print "Invalid partition name/number: %s"%part
continue
if numpart >= len(partitions) or numpart <= 2:
print "Invalid partition name/number: %s"%part
continue
break
print
print "Partition %d chosen (%s)"%(numpart+1,partitions[numpart].name)
print "Loading filesystem...",
try:
fs = FATX(partitions[numpart])
if not fs.justFormatted:
print
print "Filesystem Information:"
fs.showinfo()
print
area = fs.reserve_area()
if not area:
continue
start,length = area
start += partitions[numpart].start
print "MBR Partitions:"
mbrpt = MBRPartitionTable(device)
parts = mbrpt.gettable()
if len(parts) == 0:
print " [No MBR Partitions]"
else:
for i in mbrpt.gettable():
print " "+str(i)
while True:
pnum = raw_input("Enter the partition number which you want to create: ")
try:
pnum = int(pnum)
if pnum > 4 or pnum < 1:
raise ValueError("blah")
break
except ValueError:
print "Invalid value."
continue
if mbrpt.hasentry(pnum-1):
yesno = raw_input("Partition table entry %d already exists! Do you want to overwrite it? (Y/N): ")
if yesno in "Yy":
break
else:
break
while True:
ptype = raw_input("Enter the type for the partition (82 for Linux swap, 83 for Linux, etc): ")
try:
ptype = int(ptype,16)
if ptype == 0:
raise ValueError("blah")
break
except ValueError:
print "Invalid value."
continue
mbrpt.writeentry(pnum-1,0x00,ptype,start,length)
print "Done!"
print
partitions = PartitionCollection(device)
partitions.register(XBOXPartitionTable(device))
partitions.register(MBRPartitionTable(device))
print str(partitions)
except NonFatalFilesystemError:
t,v,tb = sys.exc_info()
print v
print
print "Press enter to continue"
raw_input()
continue
while True:
yesno = raw_input("Do you want to perform other operations? (Y/N): ")
if yesno in "YyNn":
break
if yesno in "Yy":
continue
else:
#device.dumpchanges()
yesno = raw_input("Commit changes? WARNING: THIS OPERATION IS NOT REVERSIBLE (Yes/No) (You need to type 'Yes'): ")
if yesno == "Yes":
print "COMMITTING CHANGES!"
device.commit()
print "Done."
print
print "REMEMBER TO RUN hdparm -z %s OR REBOOT LINUX TO REREAD THE PARTITION TABLE!"%argv[1]
break
if __name__=="__main__":
try:
main(sys.argv)
except MemoryError:
print
print "Out of Memory."
print "You probably tried to format or work with a large partition from an Xbox LiveCD."
print "As you probably already know, the Xbox contains only 64MB of RAM, part of which"
print "is required to run the LiveCD. subfdisk needs to keep all changes in RAM for"
print "safety, but the FAT of large partitions can easily be very large, exceeding all"
print "available RAM."
print
print "Suggestion: enable some swapspace. One good place to do that is the game cache"
print "FATX partitions (X/Y/Z). If you have just created the BRFR signature, Linux"
print "will not know about these partitions yet. Try `hdparm -z /dev/hda' or reboot your"
print "Xbox. Once you have done that, run `mkswap /dev/hda53 && swapon /dev/hda53' to"
print "enable some swap. Once you are done, you can disable swap again and reformat"
print "/dev/hda53 as FATX again, using mkfs.fatx or this program (just pick the"
print "XBOX-Y partition and you will be prompted). Make sure you only do this after"
print "disabling swap with `swapoff /dev/hda53'!"