Skip to content

Commit 8419eca

Browse files
author
Morten Delenk
committed
added romfs support
1 parent 359f31b commit 8419eca

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

ncch.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def __init__(self, f,ip):
2222
self.cryptoSec3=self.flags[3]==0x0A
2323
self.cryptoSec4=self.flags[3]==0x0B
2424
self.cryptoFixkey=self.flags[7]&1
25+
self.nocrypto=self.flags[7]&0x4
2526
self.keyY=header[:16]
2627
self.doExHeader()
2728
def addCtr(self,ctr,val):
@@ -44,6 +45,8 @@ def read(self,sectorno,sectors=1):
4445
f=self.f
4546
f.seek(sectorno*512)
4647
decdata=b''
48+
if self.nocrypto:
49+
return f.read(sectors*512)
4750
if sectorno == 0:
4851
#Sector is unencrypted
4952
data=f.read(512)

romfsfuse.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
import logging
3+
from errno import ENOENT, EIO
4+
from stat import S_IFDIR, S_IFLNK, S_IFREG
5+
from sys import argv, exit
6+
from time import time
7+
import subprocess
8+
from fuse import FUSE, FuseOSError, Operations, LoggingMixIn
9+
import struct
10+
class romfsFile:
11+
def __repr__(self):
12+
if self.isdir:
13+
s = "Directory '{}' with subdirs [".format(self.name)
14+
for subdir in self.children:
15+
s+=subdir.name+", "
16+
s+="] and files ["
17+
for fil in self.files:
18+
s+=fil.name+", "
19+
s+="]."
20+
return s
21+
return "File '{}' of size {} at {}".format(self.name,hex(self.length),hex(self.off))
22+
class RomFS(LoggingMixIn, Operations):
23+
def traverse(self):
24+
parent,sibling,child,ffile,namelen=struct.unpack("<IIIIxxxxI",self.f.read(0x18))
25+
fi = romfsFile()
26+
fi.isdir=True
27+
fi.name=self.f.read(namelen)
28+
fi.name=fi.name.decode("UTF-16")
29+
if len(fi.name) != 0:
30+
if fi.name[-1] == '\x00':
31+
fi.name=fi.name[:-1]
32+
fi.siblings=[]
33+
if sibling != 0xFFFFFFFF:
34+
self.f.seek(self.dirmetaOff+sibling)
35+
siblin = self.traverse()
36+
fi.siblings=[siblin]+siblin.siblings
37+
fi.children=[]
38+
if child != 0xFFFFFFFF:
39+
self.f.seek(self.dirmetaOff+child)
40+
chld = self.traverse()
41+
fi.children=[chld]+chld.siblings
42+
fi.files=[]
43+
if ffile != 0xFFFFFFFF:
44+
self.f.seek(self.filemetaOff+ffile)
45+
fil = self.traverse_file()
46+
fi.files=[fil]+fil.siblings
47+
return fi
48+
def traverse_file(self):
49+
parent,sibling,dataoff,datalen,namelen=struct.unpack("<IIQQxxxxI",self.f.read(0x20))
50+
fi = romfsFile()
51+
fi.isdir=False
52+
fi.name=self.f.read(namelen).decode("UTF-16")
53+
if len(fi.name) != 0:
54+
if fi.name[-1] == '\x00':
55+
fi.name=fi.name[:-1]
56+
fi.siblings=[]
57+
if sibling != 0xFFFFFFFF:
58+
self.f.seek(self.filemetaOff+sibling)
59+
siblin = self.traverse_file()
60+
fi.siblings=[siblin]+siblin.siblings
61+
fi.off=dataoff
62+
fi.length=datalen
63+
return fi
64+
def gentree(self,fi,path=""):
65+
name=path+fi.name
66+
root=False
67+
if name=="":
68+
root=True
69+
name="/"
70+
if fi.isdir:
71+
name+="/"
72+
self.files[name[:-1]]=dict(st_mode=(S_IFDIR | 0o555), st_ctime=self.now, st_mtime=self.now, st_atime=self.now, st_nlink=2)
73+
if root:
74+
name=name[:-1]
75+
for subdir in fi.children:
76+
self.gentree(subdir, name)
77+
for fil in fi.files:
78+
self.gentree(fil, name)
79+
return
80+
self.filelocs[name] = fi.off
81+
self.files[name]=dict(st_mode=(S_IFREG | 0o555), st_ctime=self.now, st_mtime=self.now, st_atime=self.now, st_nlink=2, st_size=fi.length, st_blocks=(fi.length+511)//512)
82+
def __init__(self,fname,mount):
83+
def align(a,b):
84+
if a%b:
85+
return a-(a%b)+b
86+
return a
87+
self.f=open(fname,"rb")
88+
print("Reading header")
89+
self.header=self.f.read(0x5C)
90+
masterhashsize=struct.unpack("<I",self.header[0x08:0x0C])[0]
91+
hashblocksize=1<<struct.unpack("<I",self.header[0x4C:0x50])[0]
92+
v=align(0x5C+masterhashsize,hashblocksize)
93+
self.f.seek(v)
94+
print("Reading l3 header")
95+
self.level3h=self.f.read(0x28)
96+
self.dirmetaOff,self.dirmetaSize,self.filemetaOff,self.filemetaSize,self.fileDataOff=struct.unpack("<IIxxxxxxxxIII",self.level3h[0xC:0x28])
97+
self.dirmetaOff+=v
98+
self.filemetaOff+=v
99+
self.fileDataOff+=v
100+
self.f.seek(self.dirmetaOff)
101+
self.root=self.traverse()
102+
self.filelocs={}
103+
self.fd=1
104+
self.now=time()
105+
self.files={}
106+
self.gentree(self.root)
107+
def chmod(self, path, mode):
108+
raise FuseOSError(EIO)
109+
def chown(self, path, uid, gid):
110+
raise FuseOSError(EIO)
111+
def create(self, path, mode):
112+
raise FuseOSError(EIO)
113+
def getattr(self,path,fh=None):
114+
if path not in self.files:
115+
raise FuseOSError(ENOENT)
116+
return self.files[path]
117+
def getxattr(self,path,name,position=0):
118+
return ''
119+
def listxattr(self,path):
120+
return []
121+
def mkdir(self,path,mode):
122+
raise FuseOSError(EIO)
123+
def open(self,path,flags):
124+
self.fd+=1
125+
return self.fd
126+
def read(self,path,size,offset,fh):
127+
self.f.seek(self.filelocs[path]+offset+self.fileDataOff)
128+
return self.f.read(size)
129+
def readdir(self, path, fh):
130+
print(path)
131+
files=['.','..']
132+
for fi in self.files.keys():
133+
sp=fi.split("/")
134+
if len(sp) != 2 or path != "/":
135+
if '/'.join(sp[:-1]) != path:
136+
continue
137+
if fi=="/":
138+
continue
139+
files.append(sp[-1])
140+
return files
141+
def readlink(self, path):
142+
return self.data[path]
143+
def removexattr(self, path, name):
144+
raise FuseOSError(EIO)
145+
def rename(self, old, new):
146+
raise FuseOSError(EIO)
147+
def rmdir(self, path):
148+
raise FuseOSError(EIO)
149+
def setxattr(self, path, name, value, options, position=0):
150+
raise FuseOSError(EIO)
151+
def statfs(self,path):
152+
return dict(f_bsize=512, f_blocks=4096, f_bavail=0)
153+
def symlink(self, target, source):
154+
raise FuseOSError(EIO)
155+
def truncate(self,path,length,fh=None):
156+
raise FuseOSError(EIO)
157+
def unlink(self,path):
158+
raise FuseOSError(EIO)
159+
def utimens(self,path,times=None):
160+
raise FuseOSError(EIO)
161+
def write(self,path,data,offset,fh):
162+
raise FuseOSError(EIO)
163+
def flush(self,path,fh):
164+
pass
165+
def release(self,path,fh):
166+
pass
167+
168+
if len(argv) != 3:
169+
print("usage: {name} <ROMFS> <mountpoint>".format(name=argv[0]))
170+
exit(1)
171+
logging.basicConfig(level=logging.WARNING)
172+
fuse = FUSE(RomFS(argv[1], argv[2]),argv[2], foreground=False)

0 commit comments

Comments
 (0)