Jump to content


Photo

Subfdisk


  • Please log in to reply
2 replies to this topic

#1 ldotsfan

ldotsfan

    X-S Messiah

  • Dev/Contributor
  • PipPipPipPipPipPipPip
  • 3,100 posts
  • Xbox Version:v1.1
  • 360 version:unknown

Posted 21 February 2010 - 03:37 PM

Found an interesting piece of Python Code, written by Hector Martin <hector@marcansoft.com>,
it read and writes to a xbox hdd. Wondering if XBMC's Python supports the necessary imports.

CODE

#!/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
        sector = self.device.read(self.sector)
        sig = getword(sector[0x1FE:0x200])
        if sig != 0xaa55:
            raise RuntimeError("Invalid MBR Partition Table signature: 0x%04X"%sig)
    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]
        if sig!="BRFR":
            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
        if self.bootblock[0:4] != "FATX":
            
            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
                self.bootblock = struct.pack("<4sIIHI","FATX",random.randint(0,2**32-1),32,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
&nb

#2 Pulsemasta

Pulsemasta

    X-S Expert

  • Members
  • PipPipPip
  • 725 posts
  • Xbox Version:v1.1
  • 360 version:unknown

Posted 14 March 2010 - 12:53 PM

What would be the end application of this code?

#3 ldotsfan

ldotsfan

    X-S Messiah

  • Dev/Contributor
  • PipPipPipPipPipPipPip
  • 3,100 posts
  • Xbox Version:v1.1
  • 360 version:unknown

Posted 14 March 2010 - 02:33 PM

A newer version: http://pastebin.com/BqS7xGAH , WIP, still buggy.

A multi-platform (any platform that supports Python) version of a xboxhdm-like tool, ie xbox hdd preparation.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users