..
Copyright 2019 RoadrunnerWMC
This file is part of ndspy.
ndspy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ndspy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ndspy. If not, see .
``ndspy.narc``: NARC Archives
=============================
.. py:module:: ndspy.narc
The ``ndspy.narc`` module allows you to open and save *NARC* archive files.
*NARC* archives are structured similarly to ROM files, in that each file can be
referenced by either ID or by a filename from a filename table. Filename tables
work the same as those of ROMs.
.. warning::
The default value of :py:attr:`NARC.endiannessOfBeginning` for new
:py:class:`NARC` objects is ``'<'``, but some tools, such as `Tinke
`_ and `narchive
`_, don't support that type of
*NARC* file. If you're experiencing problems with them -- or if your *NARC*
files aren't loaded correctly by the game you're working with -- try
setting it to ``'>'`` instead.
Examples
--------
Load a *NARC* from a loose file and see what's inside:
.. code-block:: python
>>> import ndspy.narc
>>> narc = ndspy.narc.NARC.fromFile('mg_trampoline.narc')
>>> print(narc)
>>>
Load a *NARC* from within a ROM, replace a file, and save it back into the ROM:
.. code-block:: python
>>> import ndspy.rom, ndspy.narc
>>> rom = ndspy.rom.NintendoDSRom.fromFile('Zelda - Phantom Hourglass.nds')
>>> narc = ndspy.narc.NARC(rom.getFileByName('Effect/effecttex.narc'))
>>> print(narc)
>>> narc.setFileByName('zds_airflow.ntfp', b'example data of some kind')
>>> rom.setFileByName('Effect/effecttex.narc', narc.save())
>>> rom.saveToFile('Zelda - Phantom Hourglass - Edited.nds')
>>>
Create a new *NARC* from scratch, and save it to a file:
.. code-block:: python
>>> import ndspy.narc, ndspy.fnt
>>> with open('a.txt', 'rb') as f:
... aTxt = f.read()
...
>>> with open('b.bin', 'rb') as f:
... bBin = f.read()
...
>>> with open('images/c.png', 'rb') as f:
... cPng = f.read()
...
>>> root = ndspy.fnt.Folder(files=['a.txt', 'b.bin'])
>>> imagesFolder = ndspy.fnt.Folder(files=['c.png'])
>>> # a.txt and b.bin come before the images/ folder. Thus, they'll have
... # IDs 0 and 1, and the images/ folder therefore needs ID 2:
...
>>> imagesFolder.firstID = 2
>>> root.folders = [('images', imagesFolder)]
>>> narc = ndspy.narc.NARC.fromFilesAndNames([aTxt, bBin, cPng], root)
>>> print(narc)
>>> narc.saveToFile('things.narc')
>>>
API
---
.. py:class:: NARC([data])
A *NARC* archive file.
:param data: The data to be read as a *NARC* file. If this is not provided,
the *NARC* object will initially be empty.
:type data: bytes
.. py:attribute:: endiannessOfBeginning
The endianness of the first 8 bytes of the *NARC* file header. The rest
of the file is always little-endian.
This option exists because different games use different header
endiannesses. For example, *The Legend of Zelda: Spirit Tracks* uses
little-endian *NARC*\s, whereas *New Super Mario Bros.* uses big-endian.
ndspy supports both, and auto-detects it when loading a *NARC* file,
but if you're creating new :py:class:`NARC` objects from scratch, it's
a good idea to set this attribute explicitly to match the game you're
working with.
``'<'`` and ``'>'`` (representing little-endian and big-endian,
respectively) are the only values this attribute is allowed to take.
:type: :py:class:`str`
:default: ``'<'``
.. py:attribute:: filenames
The root folder of the *NARC*'s filename table.
.. seealso::
:py:mod:`ndspy.fnt` -- the ndspy module the
:py:class:`ndspy.fnt.Folder` class resides in.
:py:attr:`files` -- the corresponding list of files that these
filenames refer to.
:type: :py:class:`ndspy.fnt.Folder`
:default: ``ndspy.fnt.Folder()``
.. py:attribute:: files
The list of files in this *NARC*. Indices are file IDs; that is,
":py:attr:`files`\[0]" is the file with file ID 0,
":py:attr:`files`\[1]" is the file with file ID 1, etc.
.. seealso::
:py:attr:`filenames` -- the set of filenames for these files.
:type: :py:class:`list` of :py:class:`bytes`
:default: ``[]``
.. py:classmethod:: fromFilesAndNames(files[, filenames])
Create a *NARC* archive from a list of files and (optionally) a
filename table.
:param files: The initial value for the :py:attr:`files` attribute.
:param filenames: The initial value for the :py:attr:`filenames`
attribute.
:returns: The *NARC* object.
:rtype: :py:class:`NARC`
.. py:classmethod:: fromFile(filePath)
Load a *NARC* archive from a filesystem file. This is a convenience
function.
:param filePath: The path to the *NARC* file to open.
:type filePath: :py:class:`str` or other path-like object
:returns: The *NARC* object.
:rtype: :py:class:`NARC`
.. py:function:: getFileByName(filename)
Return the data for the file with the given filename (path). This is a
convenience function; the following two lines of code are exactly
equivalent (apart from some error checking):
.. code-block:: python
fileData = narc.getFileByName(filename)
fileData = narc.files[narc.filenames.idOf(filename)]
.. seealso::
:py:func:`setFileByName` -- to replace the file data instead of
retrieving it.
:param str filename: The name of the file.
:returns: The file's data.
:rtype: :py:class:`bytes`
.. py:function:: setFileByName(filename, data)
Replace the data for the file with the given filename (path) with the
given data. This is a convenience function; the following two lines of
code are exactly equivalent (apart from some error checking):
.. code-block:: python
narc.setFileByName(filename, fileData)
narc.files[narc.filenames.idOf(filename)] = fileData
.. seealso::
:py:func:`getFileByName` -- to retrieve the file data
instead of replacing it.
:param str filename: The name of the file.
:param bytes data: The new data for the file.
.. py:function:: save()
Generate file data representing this *NARC*.
:returns: The *NARC* archive file data.
:rtype: :py:class:`bytes`
.. py:function:: saveToFile(filePath)
Generate file data representing this *NARC*, and save it to a
filesystem file. This is a convenience function.
:param filePath: The path to the *NARC* archive file to save to.
:type filePath: :py:class:`str` or other path-like object