Skip to content

Commit

Permalink
Merge pull request #61 from Robert-N7/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Robert-N7 authored May 24, 2021
2 parents 3f70fc3 + 261e31c commit 46080af
Show file tree
Hide file tree
Showing 81 changed files with 1,458 additions and 635 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ abmatt [command_line][--interactive -f <file> -b <brres-file> -d <destination> -
| -i | --interactive | Interactive shell mode. |
| -l | --loudness | Sets the verbosity level. (0-5)
| -o | --overwrite | Overwrite existing files. |
| | --moonview | Treat the Brres as Moonview course, adjusting material names. |

### Command Line Examples
This command would open *course_model.brres* in overwrite mode and run the commands stored in *my_commands.txt*
Expand Down Expand Up @@ -89,7 +90,7 @@ line = begin_preset | command_line;
begin_preset = '[' <preset_name> ']' EOL;
command_line = cmd-prefix ['for' selection] EOL;
cmd-prefix = set | info | add | remove | select | preset | save | copy | paste | convert;
cmd-prefix = set | info | add | remove | select | preset | save | copy | paste | convert | load;
set = 'set' type setting;
info = 'info' type [key | 'keys'];
add = 'add' type;
Expand All @@ -100,6 +101,7 @@ save = 'save' [filename] ['as' destination] ['overwrite']
copy = 'copy' type;
paste = 'paste' type;
convert = 'convert' filename ['to' destination] ['--no-colors'] ['--no-normals'] ['--single-bone'] ['--no-uvs']
load = 'load' command-file
selection = name ['in' container]
container = ['brres' filename] ['model' name];
Expand Down Expand Up @@ -241,10 +243,10 @@ tex0-format = 'cmpr' | 'c14x2' | 'c8' | 'c4' | 'rgba32' | 'rgb5a3' | 'rgb565'
| 'ia8' | 'ia4' | 'i8' | 'i4';
```

### Presets
### Presets and Command Files
Presets are a way of grouping commands together. They can be defined in `presets.txt` or in command files.
Presets begin with `[<preset_name>]` and include all commands until another preset is encountered or end of file.
An empty preset `[]` can be used to stop preset parsing.
An empty preset `[]` can be used to stop preset parsing (and begin command parsing).
```
[my_preset]
set material xlu:True
Expand All @@ -254,5 +256,9 @@ set layer mapmode:linear_mipmap_linear
To call the preset:
`preset my_preset for my_material_name`

The `load` command can be used to load additional commands and presets.
As with all recursive things, be careful not to create an infinite loop!

## Known Limitations and Bugs
* Windows installer sometimes hangs in the background until the process is terminated.
* Windows installer sometimes hangs in the background until the process is terminated.
* Non-standard files in Brres are not supported.
2 changes: 1 addition & 1 deletion abmatt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def main():
base_path = os.path.abspath(__file__)
files = load_config.parse_args(argv, base_path)
# cleanup
for file in files.values():
for file in files:
file.close()


Expand Down
26 changes: 17 additions & 9 deletions abmatt/autofix.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def __init__(self, fix_level=3, loudness=3):
if self.__AUTO_FIXER: raise RuntimeError('Autofixer already initialized')
self.loudness = loudness
self.fix_level = fix_level
self.zero_level_func = None
self.queue = []
self.is_running = False
self.pipe = None # if set, output is sent to the pipe, must implement info warn and error.
Expand Down Expand Up @@ -151,15 +152,22 @@ def get(fixe_level=3, loudness=3):
def set_pipe(self, obj):
self.pipe = obj

def set_fix_level(self, fix_level):
try:
self.fix_level = int(fix_level)
except ValueError:
fix_level = fix_level.upper()
if fix_level == 'ALL':
self.fix_level = 4
else:
self.fix_level = self.ERROR_LEVELS.index(fix_level)
def set_fix_level(self, fix_level, zero_level_func=None):
if zero_level_func:
self.zero_level_func = zero_level_func
if type(fix_level) is int:
self.fix_level = fix_level
elif fix_level is not None:
try:
self.fix_level = int(fix_level)
except ValueError:
fix_level = fix_level.upper()
if fix_level == 'ALL':
self.fix_level = 4
elif fix_level in self.ERROR_LEVELS:
self.fix_level = self.ERROR_LEVELS.index(fix_level)
if fix_level == 0 and self.zero_level_func:
self.zero_level_func()

def can_prompt(self):
return self.fix_level == self.FIX_PROMPT
Expand Down
63 changes: 53 additions & 10 deletions abmatt/brres/brres.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@
# Brres Class
# --------------------------------------------------------
import os
import string

from abmatt.autofix import AutoFix, Bug
from abmatt.brres.lib.binfile import BinFile
from abmatt.brres.lib.matching import MATCHING
from abmatt.brres.lib.node import Clipable, Packable
from abmatt.brres.lib.packing.pack_brres import PackBrres
from abmatt.brres.lib.unpacking.unpack_brres import UnpackBrres
from abmatt.brres.mdl0.material.material import Material
from abmatt.brres.tex0 import Tex0
from abmatt.image_converter import ImgConverter


class Brres(Clipable, Packable):

SETTINGS = ('name',)
MAGIC = 'bres'
OVERWRITE = False
DESTINATION = None
OPEN_FILES = [] # reference to active files
REMOVE_UNUSED_TEXTURES = False
MOONVIEW = False # if true, treat brres as moonview

def __init__(self, name, parent=None, read_file=True):
"""
Expand All @@ -37,12 +39,13 @@ def __init__(self, name, parent=None, read_file=True):
self.models = []
self.texture_map = {}
self.textures = []
self.srt0 = []
self.pat0 = []
self.unused_srt0 = None
self.unused_pat0 = None
self.chr0 = []
self.scn0 = []
self.shp0 = []
self.clr0 = []
self.unknown = []
binfile = BinFile(name) if read_file else None
super(Brres, self).__init__(name, parent, binfile)
self.add_open_file(self)
Expand Down Expand Up @@ -76,6 +79,20 @@ def get_brres(filename, create_if_not_exists=False):
def begin(self):
self.is_modified = True

def respect_model_names(self):
names = {x.name.rstrip(string.digits) for x in self.models}
return len(names) != len(self.models)

def __hash__(self):
return hash(self.name)

def __eq__(self, other):
return other is not None and type(other) == Brres and self.models == other.models \
and self.texture_map == other.texture_map \
and self.unused_srt0 == other.unused_srt0 and self.unused_pat0 == other.unused_pat0 \
and self.chr0 == other.chr0 and self.scn0 == other.scn0 and self.shp0 == other.shp0 \
and self.clr0 == other.clr0 and self.unknown == other.unknown

def get_str(self, key):
if key == 'name':
return self.name
Expand Down Expand Up @@ -107,10 +124,6 @@ def remove_mdl0(self, name):
for x in self.models:
if x.name == name:
self.models.remove(x)
if x.srt0_collection:
self.srt0.remove(x.srt0_collection)
if x.pat0_collection:
self.pat0.remove(x.pat0_collection)
self.mark_modified()
break

Expand Down Expand Up @@ -180,7 +193,7 @@ def get_trace(self):

def info(self, key=None, indentation_level=0):
AutoFix.info('{}{}:\t{} model(s)\t{} texture(s)'.format(' ' * indentation_level + '>',
self.name, len(self.models), len(self.textures)), 1)
self.name, len(self.models), len(self.textures)), 1)
indentation_level += 2
self.sub_info('MDL0', self.models, key, indentation_level)
self.sub_info('TEX0', self.textures, key, indentation_level)
Expand Down Expand Up @@ -223,7 +236,7 @@ def get_expected_brres_fname(filename):
return os.path.join(w_dir, name + '.brres')

def get_expected_mdl_name(self):
filename = self.name
filename = os.path.basename(self.name)
for item in ('course', 'map', 'vrcorn'):
if item in filename:
return item
Expand Down Expand Up @@ -264,7 +277,7 @@ def add_tex0(self, tex0, replace=True, mark_modified=True):
tex0 = t
self.textures.append(tex0)
self.texture_map[tex0.name] = tex0
tex0.parent = self # this may be redundant
tex0.parent = self # this may be redundant
if mark_modified:
self.mark_modified()
return True
Expand Down Expand Up @@ -353,6 +366,9 @@ def pack(self, binfile):
def check(self):
AutoFix.info('checking file {}'.format(self.name), 4)
expected = self.get_expected_mdl_name()
if self.MOONVIEW or 'ridgehighway_course' in self.name:
self.check_moonview()
Brres.MOONVIEW = False
for mdl in self.models:
mdl.check(expected)
expected = None
Expand All @@ -369,6 +385,33 @@ def check(self):
for tex in all_tex:
tex.check()

def check_moonview(self):
if not self.models:
return True
mat_names = ['Goal_Merg', 'Iwa', 'Iwa_alfa', 'Nuki_Ryoumen', 'WallMerg00',
'moon_kabe0000', 'moon_road00', 'road', 'road01', 'road02', 'road03',
'siba00']
materials = self.models[0].materials
# First check if there's any modification needed
j = 0
for i in range(len(materials)):
if materials[i].name == mat_names[j]:
j += 1
# Now rename
if j != len(mat_names):
b = Bug(3, 3, 'Incorrect material names for ridgehighway_course', 'Renaming materials')
for i in range(len(mat_names)):
if i < len(materials):
if materials[i].name != mat_names[i]:
material = self.models[0].get_material_by_name(mat_names[i])
if material:
material.rename(Material.get_unique_material('material', self.models[0], get_name_only=True))
materials[i].rename(mat_names[i])
else:
self.models[0].add_material(Material.get_unique_material(mat_names[i], self.models[0]))
b.resolve()
return j != len(mat_names)

def remove_unused_textures(self, unused_textures):
tex = self.textures
tex_map = self.texture_map
Expand Down
17 changes: 17 additions & 0 deletions abmatt/brres/chr0.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ def __init__(self, name, parent, binfile=None, framecount=1, loop=True):
# self.offset = offset # since we don't parse data... store name offsetg
super().__init__(name, parent, binfile)

def __eq__(self, other):
return super().__eq__(other) and self.framecount == other.framecount and self.loop == other.loop \
and self.x_translation == other.x_translation and self.y_translation == other.y_translation \
and self.z_translation == other.z_translation and self.x_rotation == other.x_rotation \
and self.y_rotation == other.y_rotation and self.z_rotation == other.z_rotation \
and self.x_scale == other.x_scale and self.y_scale == other.y_scale and self.z_scale == other.z_scale

def __iter__(self):
return iter(self.animations)

def __next__(self):
return next(self.animations)

def __eq__(self, other):
return super().__eq__(other) and self.framecount == other.framecount and self.loop == other.loop \
and self.scaling_rule == other.scaling_rule and self.animations == other.animations

def set_str(self, key, value):
return set_anim_str(self, key, value)

Expand Down
3 changes: 3 additions & 0 deletions abmatt/brres/clr0/clr0.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ def __init__(self, name, parent, binfile=None):
self.animations = []
super(Clr0, self).__init__(name, parent, binfile)

def __eq__(self, other):
return super().__eq__(other) and self.framecount == other.framecount and self.animations == other.animations

def begin(self, initial_values=None):
self.loop = True
self.framecount = 1
Expand Down
19 changes: 14 additions & 5 deletions abmatt/brres/clr0/clr0_animation.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
class Clr0Animation:
from abmatt.brres.lib.node import Node

def __init__(self, name, framecount=1, loop=True):

class Clr0Animation(Node):

def __init__(self, name, parent):
self.name = name
self.framecount = framecount
self.loop=loop
self.framecount = parent.framecount
self.loop = parent.loop
self.flags = [False] * 16
self.is_constant = [False] * 16
self.entry_masks = []
self.entries = [] # can be fixed (if constant) otherwise key frame list
self.entries = [] # can be fixed (if constant) otherwise key frame list
super().__init__(name, parent)

def __eq__(self, other):
return super().__eq__(other) and self.framecount == other.framecount and self.loop == other.loop \
and self.flags == other.flags and self.is_constant == other.is_constant \
and self.entry_masks == other.entry_masks and self.entries == other.entries
21 changes: 11 additions & 10 deletions abmatt/brres/lib/binfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(self, filename, mode='r', bom='>'):
"""
self.beginOffset = self.offset = 0
self.filename = filename
self.names_offset = float('inf')
self.stack = [] # used for tracking depth in files
self.references = {} # used for forward references in relation to start
self.bom = bom # byte order mark > | <
Expand Down Expand Up @@ -388,6 +389,8 @@ def unpack_name(self, advance=True):
if name:
return name
try:
if offset - 4 < self.names_offset:
self.names_offset = offset - 4
[name_lens] = self.readOffset("I", offset - 4)
except struct.error:
raise UnpackingError(self, 'Incorrect name offset')
Expand Down Expand Up @@ -428,7 +431,7 @@ def packNames(self):
# with open('names.txt', 'w') as f:
# f.write(str(out))
#- end debug

self.names_offset = self.offset
for key in sorted(names):
if key is not None and key != b'':
self.align(4)
Expand Down Expand Up @@ -594,8 +597,7 @@ def addEntry(self, name, dataPtr=0):
def unpack(self, binfile):
""" Unpacks folder """
# print('Folder {} offset {}'.format(self.name, binfile.offset))
binfile.start()
self.offset = binfile.offset
self.offset = binfile.start()
len, num_entries = binfile.read("2I", 8)
binfile.advance(16) # skip first entry
# first = FolderEntry(self, 0)
Expand All @@ -609,8 +611,7 @@ def unpack(self, binfile):

def pack(self, binfile):
""" packs folder """
binfile.start()
self.offset = binfile.offset
self.offset = binfile.start()
entries = self.calcEntries()
binfile.write("2I", self.byteSize(), len(entries) - 1) # -1 to ignore reference entry
for x in entries:
Expand Down Expand Up @@ -661,11 +662,11 @@ def createEntryRef(self, name):
return self.createEntryRefI(i)
raise PackingError(self.binfile, "Entry name {} not in folder {}".format(name, self.name))

def createEntryRefI(self, index=0):
""" creates reference in folder to section at entry[index] (once only, pops)"""
entry = self.entries.pop(index)
# print('{} {}'.format(self.binfile.offset, entry.name))
return self.binfile.createRefFrom(self.offset, index + 1) # index + 1 ignoring the first ref entry
def createEntryRefI(self, index=0, pop=True):
""" creates reference in folder to section at entry[index]"""
if pop:
entry = self.entries.pop(index)
return self.binfile.createRefFrom(self.offset, index + 1, pop=pop) # index + 1 ignoring the first ref entry


def printCollectionHex(collection):
Expand Down
Loading

0 comments on commit 46080af

Please sign in to comment.