oliver_g avatar oliver_g committed a9fae7c

nds parser: switch to RootSeekableFieldSet for better results

This improves parsing of weird ROMs (eg. with different ordering),
and adds support for extracting embedded data files.

Also, filename_table is now optional.

Comments (0)

Files changed (2)

hachoir-parser/hachoir_parser/program/nds.py

 
 from hachoir_parser import Parser
 from hachoir_core.field import (ParserError,
-    UInt8, UInt16, UInt32, UInt64, String, RawBytes, FieldSet, NullBits, Bit, Bits)
+    UInt8, UInt16, UInt32, UInt64, String, RawBytes, SubFile, FieldSet, NullBits, Bit, Bits,
+    SeekableFieldSet, RootSeekableFieldSet)
 from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
 
 class FileNameDirTable(FieldSet):
         yield UInt16(self, "header_crc16")
 
 
-class NdsFile(Parser):
+class NdsFile(Parser, RootSeekableFieldSet):
     PARSER_TAGS = {
         "id": "nds_file",
         "category": "program",
         yield Header(self, "header")
 
         # ARM9 binary
-        if self["header"]["arm9_source"].value - (self.current_size / 8) > 0:
-            yield RawBytes(self, "pad[]", self["header"]["arm9_source"].value - (self.current_size / 8))
+        self.seekByte(self["header"]["arm9_source"].value, relative=False)
         yield RawBytes(self, "arm9_bin", self["header"]["arm9_bin_size"].value)
 
         # ARM7 binary
-        if self["header"]["arm7_source"].value - (self.current_size / 8) > 0:
-            yield RawBytes(self, "pad[]", self["header"]["arm7_source"].value - (self.current_size / 8))
+        self.seekByte(self["header"]["arm7_source"].value, relative=False)
         yield RawBytes(self, "arm7_bin", self["header"]["arm7_bin_size"].value)
 
         # File Name Table
-        if self["header"]["filename_table_offset"].value - (self.current_size / 8) > 0:
-            yield RawBytes(self, "pad[]", self["header"]["filename_table_offset"].value - (self.current_size / 8))
-        yield FileNameTable(self, "filename_table")
+        if self["header"]["filename_table_size"].value > 0:
+            self.seekByte(self["header"]["filename_table_offset"].value, relative=False)
+            yield FileNameTable(self, "filename_table", size=self["header"]["filename_table_size"].value*8)
 
         # FAT
         if self["header"]["fat_size"].value > 0:
-            if self["header"]["fat_offset"].value - (self.current_size / 8) > 0:
-                yield RawBytes(self, "pad[]", self["header"]["fat_offset"].value - (self.current_size / 8))
-            yield FATContent(self, "fat_content")
+            self.seekByte(self["header"]["fat_offset"].value, relative=False)
+            yield FATContent(self, "fat_content", size=self["header"]["fat_size"].value*8)
 
         # banner
         if self["header"]["banner_offset"].value > 0:
-            if self["header"]["banner_offset"].value - (self.current_size / 8) > 0:
-                yield RawBytes(self, "pad[]", self["header"]["banner_offset"].value - (self.current_size / 8))
+            self.seekByte(self["header"]["banner_offset"].value, relative=False)
             yield Banner(self, "banner")
 
-        # Read rest of the file (if any)
-        if self.current_size < self._size:
-            yield self.seekBit(self._size, "end")
+        # files
+        if self["header"]["fat_size"].value > 0:
+            for field in self["fat_content"]:
+                if field["end"].value > field["start"].value:
+                    self.seekByte(field["start"].value, relative=False)
+                    yield SubFile(self, "file[]", field["end"].value - field["start"].value)

hachoir-parser/tests/run_testcase.py

     checkValue(parser, "/header/game_title", '.'),
     checkValue(parser, "/header/header_crc16", 29398),
     checkValue(parser, "/filename_table/directory[4]/entry[0]/name", "file2.txt"),
+    checkValue(parser, "/file[1]", "Hello from file2.txt\n\n"),
 )
 
 def checkFile(filename, check_parser):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.