summaryrefslogtreecommitdiff
path: root/Puzzle+
diff options
context:
space:
mode:
authorColin Noga <Tempus@Spectrum-Song.local>2011-11-04 22:02:51 -0500
committerColin Noga <Tempus@Spectrum-Song.local>2011-11-04 22:02:51 -0500
commit38e50fb9870ff1b253a28ba6143339727a5b476f (patch)
tree0942eb71430894c12992a0179f94cc629b69ff03 /Puzzle+
parentc4e5f6b3191498e273965224eb827bf83966d3ff (diff)
downloadkoopatlas-38e50fb9870ff1b253a28ba6143339727a5b476f.tar.gz
koopatlas-38e50fb9870ff1b253a28ba6143339727a5b476f.zip
Koopuzzle now seems to be working 100%..., included a testing tileset called Test5.arc that covers all group use cases.
Diffstat (limited to 'Puzzle+')
-rw-r--r--Puzzle+/.DS_Storebin6148 -> 0 bytes
-rwxr-xr-xPuzzle+/archive.py205
-rwxr-xr-xPuzzle+/common.py364
-rwxr-xr-xPuzzle+/license.txt339
-rw-r--r--Puzzle+/nsmblib-0.5a.zipbin9056 -> 0 bytes
-rwxr-xr-xPuzzle+/puzzle+.py1858
-rwxr-xr-xPuzzle+/windows_build.py81
7 files changed, 0 insertions, 2847 deletions
diff --git a/Puzzle+/.DS_Store b/Puzzle+/.DS_Store
deleted file mode 100644
index 5008ddf..0000000
--- a/Puzzle+/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/Puzzle+/archive.py b/Puzzle+/archive.py
deleted file mode 100755
index b0495f5..0000000
--- a/Puzzle+/archive.py
+++ /dev/null
@@ -1,205 +0,0 @@
-from common import *
-
-
-class U8(WiiArchive):
- class U8Header(Struct):
- __endian__ = Struct.BE
- def __format__(self):
- self.tag = Struct.string(4)
- self.rootnode_offset = Struct.uint32
- self.header_size = Struct.uint32
- self.data_offset = Struct.uint32
- self.zeroes = Struct.string(16)
- class U8Node(Struct):
- __endian__ = Struct.BE
- def __format__(self):
- self.type = Struct.uint16
- self.name_offset = Struct.uint16
- self.data_offset = Struct.uint32
- self.size = Struct.uint32
- def __init__(self):
- self.files = []
- def _dump(self):
- header = self.U8Header()
- rootnode = self.U8Node()
-
- # constants
- header.tag = "U\xAA8-"
- header.rootnode_offset = 0x20
- header.zeroes = "\x00" * 16
- rootnode.type = 0x0100
-
- nodes = []
- strings = '\x00'
- data = ''
-
- for item, value in self.files:
- node = self.U8Node()
-
- recursion = item.count('/')
- if(recursion < 0):
- recursion = 0
- name = item[item.rfind('/') + 1:]
-
- node.name_offset = len(strings)
- strings += name + '\x00'
-
- if(value == None): # directory
- node.type = 0x0100
- node.data_offset = recursion
-
- node.size = len(nodes) + 1
- for one, two in self.files:
- if(one[:len(item)] == item): # find nodes in the folder
- node.size += 1
- else: # file
- node.type = 0x0000
- node.data_offset = len(data)
- #print "before: " + str(len(data))
- data += value + ('\x00' * (align(len(value), 32) - len(value))) # 32 seems to work best for fuzzyness? I'm still really not sure
- #print "after: " + str(len(data))
- node.size = len(value)
- #print "sz: " + str(len(value))
- nodes.append(node)
-
- header.header_size = ((len(nodes) + 1) * len(rootnode)) + len(strings)
- header.data_offset = align(header.header_size + header.rootnode_offset, 64)
- rootnode.size = len(nodes) + 1
-
- for i in range(len(nodes)):
- if(nodes[i].type == 0x0000):
- nodes[i].data_offset += header.data_offset
-
- fd = ''
- fd += header.pack()
- fd += rootnode.pack()
- for node in nodes:
- fd += node.pack()
- fd += strings
- fd += "\x00" * (header.data_offset - header.rootnode_offset - header.header_size)
- fd += data
-
- return fd
- def _dumpDir(self, dir):
- if(not os.path.isdir(dir)):
- os.mkdir(dir)
- old = os.getcwd()
- os.chdir(dir)
- for item, data in self.files:
- if(data == None):
- if(not os.path.isdir(item)):
- os.mkdir(item)
- else:
- open(item, "wb").write(data)
- os.chdir(old)
- def _loadDir(self, dir):
- try:
- self._tmpPath += ''
- except:
- self._tmpPath = ''
- old = os.getcwd()
- os.chdir(dir)
- entries = os.listdir(".")
- for entry in entries:
- if(os.path.isdir(entry)):
- self.files.append((self._tmpPath + entry, None))
- self._tmpPath += entry + '/'
- self._loadDir(entry)
- elif(os.path.isfile(entry)):
- data = open(entry, "rb").read()
- self.files.append((self._tmpPath + entry, data))
- os.chdir(old)
- self._tmpPath = self._tmpPath[:self._tmpPath.find('/') + 1]
- def _load(self, data):
- offset = 0
-
- for i in range(len(data)):
- header = self.U8Header()
- header.unpack(data[offset:offset + len(header)])
- if(header.tag == "U\xAA8-"):
- break
- data = data[1:]
- offset += len(header)
- offset = header.rootnode_offset
-
- #print header.rootnode_offset
- #print header.header_size
- #print header.data_offset
-
- rootnode = self.U8Node()
- rootnode.unpack(data[offset:offset + len(rootnode)])
- offset += len(rootnode)
-
- nodes = []
- for i in range(rootnode.size - 1):
- node = self.U8Node()
- node.unpack(data[offset:offset + len(node)])
- offset += len(node)
- nodes.append(node)
-
- strings = data[offset:offset + header.data_offset - len(header) - (len(rootnode) * rootnode.size)]
- offset += len(strings)
-
- recursion = [rootnode.size]
- recursiondir = []
- counter = 0
- for node in nodes:
- counter += 1
- name = strings[node.name_offset:].split('\0', 1)[0]
-
- if(node.type == 0x0100): # folder
- recursion.append(node.size)
- recursiondir.append(name)
- #assert len(recursion) == node.data_offset + 2 # haxx
- self.files.append(('/'.join(recursiondir), None))
-
- #print "Dir: " + name
- elif(node.type == 0): # file
- self.files.append(('/'.join(recursiondir) + '/' + name, data[node.data_offset:node.data_offset + node.size]))
- offset += node.size
-
- #print "File: " + name
- else: # unknown type -- wtf?
- pass
-
- #print "Data Offset: " + str(node.data_offset)
- #print "Size: " + str(node.size)
- #print "Name Offset: " + str(node.name_offset)
- #print ""
-
- sz = recursion.pop()
- if(sz != counter + 1):
- recursion.append(sz)
- else:
- recursiondir.pop()
- def __str__(self):
- ret = ''
- for key, value in self.files:
- name = key[key.rfind('/') + 1:]
- recursion = key.count('/')
- ret += ' ' * recursion
- if(value == None):
- ret += '[' + name + ']'
- else:
- ret += name
- ret += '\n'
- return ret
- def __getitem__(self, key):
- for item, val in self.files:
- if(item == key):
- if(val != None):
- return val
- else:
- ret = []
- for item2, val2 in self.files:
- if(item2.find(item) == 0):
- ret.append(item2[len(item) + 1:])
- return ret[1:]
- raise KeyError
- def __setitem__(self, key, val):
- for i in range(len(self.files)):
- if(self.files[i][0] == key):
- self.files[i] = (self.files[i][0], val)
- return
- self.files.append((key, val))
-
diff --git a/Puzzle+/common.py b/Puzzle+/common.py
deleted file mode 100755
index 792acec..0000000
--- a/Puzzle+/common.py
+++ /dev/null
@@ -1,364 +0,0 @@
-import os.path, struct, sys
-
-
-class StructType(tuple):
- def __getitem__(self, value):
- return [self] * value
- def __call__(self, value, endian='<'):
- if isinstance(value, str):
- return struct.unpack(endian + tuple.__getitem__(self, 0), value[:tuple.__getitem__(self, 1)])[0]
- else:
- return struct.pack(endian + tuple.__getitem__(self, 0), value)
-
-class StructException(Exception):
- pass
-
-class Struct(object):
- __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__')
- int8 = StructType(('b', 1))
- uint8 = StructType(('B', 1))
-
- int16 = StructType(('h', 2))
- uint16 = StructType(('H', 2))
-
- int32 = StructType(('l', 4))
- uint32 = StructType(('L', 4))
-
- int64 = StructType(('q', 8))
- uint64 = StructType(('Q', 8))
-
- float = StructType(('f', 4))
-
- def string(cls, len, offset=0, encoding=None, stripNulls=False, value=''):
- return StructType(('string', (len, offset, encoding, stripNulls, value)))
- string = classmethod(string)
-
- LE = '<'
- BE = '>'
- __endian__ = '<'
-
- def __init__(self, func=None, unpack=None, **kwargs):
- self.__defs__ = []
- self.__sizes__ = []
- self.__attrs__ = []
- self.__values__ = {}
- self.__next__ = True
- self.__baked__ = False
-
- if func == None:
- self.__format__()
- else:
- sys.settrace(self.__trace__)
- func()
- for name in func.func_code.co_varnames:
- value = self.__frame__.f_locals[name]
- self.__setattr__(name, value)
-
- self.__baked__ = True
-
- if unpack != None:
- if isinstance(unpack, tuple):
- self.unpack(*unpack)
- else:
- self.unpack(unpack)
-
- if len(kwargs):
- for name in kwargs:
- self.__values__[name] = kwargs[name]
-
- def __trace__(self, frame, event, arg):
- self.__frame__ = frame
- sys.settrace(None)
-
- def __setattr__(self, name, value):
- if name in self.__slots__:
- return object.__setattr__(self, name, value)
-
- if self.__baked__ == False:
- if not isinstance(value, list):
- value = [value]
- attrname = name
- else:
- attrname = '*' + name
-
- self.__values__[name] = None
-
- for sub in value:
- if isinstance(sub, Struct):
- sub = sub.__class__
- try:
- if issubclass(sub, Struct):
- sub = ('struct', sub)
- except TypeError:
- pass
- type_, size = tuple(sub)
- if type_ == 'string':
- self.__defs__.append(Struct.string)
- self.__sizes__.append(size)
- self.__attrs__.append(attrname)
- self.__next__ = True
-
- if attrname[0] != '*':
- self.__values__[name] = size[3]
- elif self.__values__[name] == None:
- self.__values__[name] = [size[3] for val in value]
- elif type_ == 'struct':
- self.__defs__.append(Struct)
- self.__sizes__.append(size)
- self.__attrs__.append(attrname)
- self.__next__ = True
-
- if attrname[0] != '*':
- self.__values__[name] = size()
- elif self.__values__[name] == None:
- self.__values__[name] = [size() for val in value]
- else:
- if self.__next__:
- self.__defs__.append('')
- self.__sizes__.append(0)
- self.__attrs__.append([])
- self.__next__ = False
-
- self.__defs__[-1] += type_
- self.__sizes__[-1] += size
- self.__attrs__[-1].append(attrname)
-
- if attrname[0] != '*':
- self.__values__[name] = 0
- elif self.__values__[name] == None:
- self.__values__[name] = [0 for val in value]
- else:
- try:
- self.__values__[name] = value
- except KeyError:
- raise AttributeError(name)
-
- def __getattr__(self, name):
- if self.__baked__ == False:
- return name
- else:
- try:
- return self.__values__[name]
- except KeyError:
- raise AttributeError(name)
-
- def __len__(self):
- ret = 0
- arraypos, arrayname = None, None
-
- for i in range(len(self.__defs__)):
- sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
-
- if sdef == Struct.string:
- size, offset, encoding, stripNulls, value = size
- if isinstance(size, str):
- size = self.__values__[size] + offset
- elif sdef == Struct:
- if attrs[0] == '*':
- if arrayname != attrs:
- arrayname = attrs
- arraypos = 0
- size = len(self.__values__[attrs[1:]][arraypos])
- size = len(self.__values__[attrs])
-
- ret += size
-
- return ret
-
- def unpack(self, data, pos=0):
- for name in self.__values__:
- if not isinstance(self.__values__[name], Struct):
- self.__values__[name] = None
- elif self.__values__[name].__class__ == list and len(self.__values__[name]) != 0:
- if not isinstance(self.__values__[name][0], Struct):
- self.__values__[name] = None
-
- arraypos, arrayname = None, None
-
- for i in range(len(self.__defs__)):
- sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
-
- if sdef == Struct.string:
- size, offset, encoding, stripNulls, value = size
- if isinstance(size, str):
- size = self.__values__[size] + offset
-
- temp = data[pos:pos+size]
- if len(temp) != size:
- raise StructException('Expected %i byte string, got %i' % (size, len(temp)))
-
- if encoding != None:
- temp = temp.decode(encoding)
-
- if stripNulls:
- temp = temp.rstrip('\0')
-
- if attrs[0] == '*':
- name = attrs[1:]
- if self.__values__[name] == None:
- self.__values__[name] = []
- self.__values__[name].append(temp)
- else:
- self.__values__[attrs] = temp
- pos += size
- elif sdef == Struct:
- if attrs[0] == '*':
- if arrayname != attrs:
- arrayname = attrs
- arraypos = 0
- name = attrs[1:]
- self.__values__[attrs][arraypos].unpack(data, pos)
- pos += len(self.__values__[attrs][arraypos])
- arraypos += 1
- else:
- self.__values__[attrs].unpack(data, pos)
- pos += len(self.__values__[attrs])
- else:
- values = struct.unpack(self.__endian__+sdef, data[pos:pos+size])
- pos += size
- j = 0
- for name in attrs:
- if name[0] == '*':
- name = name[1:]
- if self.__values__[name] == None:
- self.__values__[name] = []
- self.__values__[name].append(values[j])
- else:
- self.__values__[name] = values[j]
- j += 1
-
- return self
-
- def pack(self):
- arraypos, arrayname = None, None
-
- ret = ''
- for i in range(len(self.__defs__)):
- sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
-
- if sdef == Struct.string:
- size, offset, encoding, stripNulls, value = size
- if isinstance(size, str):
- size = self.__values__[size]+offset
-
- if attrs[0] == '*':
- if arrayname != attrs:
- arraypos = 0
- arrayname = attrs
- temp = self.__values__[attrs[1:]][arraypos]
- arraypos += 1
- else:
- temp = self.__values__[attrs]
-
- if encoding != None:
- temp = temp.encode(encoding)
-
- temp = temp[:size]
- ret += temp + ('\0' * (size - len(temp)))
- elif sdef == Struct:
- if attrs[0] == '*':
- if arrayname != attrs:
- arraypos = 0
- arrayname = attrs
- ret += self.__values__[attrs[1:]][arraypos].pack()
- arraypos += 1
- else:
- ret += self.__values__[attrs].pack()
- else:
- values = []
- for name in attrs:
- if name[0] == '*':
- if arrayname != name:
- arraypos = 0
- arrayname = name
- values.append(self.__values__[name[1:]][arraypos])
- arraypos += 1
- else:
- values.append(self.__values__[name])
-
- ret += struct.pack(self.__endian__+sdef, *values)
- return ret
-
- def __getitem__(self, value):
- return [('struct', self.__class__)] * value
-
-
-class WiiObject(object):
- def load(cls, data, *args, **kwargs):
- self = cls()
- self._load(data, *args, **kwargs)
- return self
- load = classmethod(load)
-
- def loadFile(cls, filename, *args, **kwargs):
- return cls.load(open(filename, "rb").read(), *args, **kwargs)
- loadFile = classmethod(loadFile)
-
- def dump(self, *args, **kwargs):
- return self._dump(*args, **kwargs)
- def dumpFile(self, filename, *args, **kwargs):
- open(filename, "wb").write(self.dump(*args, **kwargs))
- return filename
-
-
-class WiiArchive(WiiObject):
- def loadDir(cls, dirname):
- self = cls()
- self._loadDir(dirname)
- return self
- loadDir = classmethod(loadDir)
-
- def dumpDir(self, dirname):
- if(not os.path.isdir(dirname)):
- os.mkdir(dirname)
- self._dumpDir(dirname)
- return dirname
-
-
-class WiiHeader(object):
- def __init__(self, data):
- self.data = data
- def addFile(self, filename):
- open(filename, "wb").write(self.add())
- def removeFile(self, filename):
- open(filename, "wb").write(self.remove())
- def loadFile(cls, filename, *args, **kwargs):
- return cls(open(filename, "rb").read(), *args, **kwargs)
- loadFile = classmethod(loadFile)
-
-
-
-def align(x, boundary):
- while x % boundary != 0:
- x += 1
- return x
-
-def clamp(var, min, max):
- if var < min: var = min
- if var > max: var = max
- return var
-
-def abs(var):
- if var < 0:
- var = var + (2 * var)
- return var
-
-def hexdump(s, sep=" "): # just dumps hex values
- return sep.join(map(lambda x: "%02x" % ord(x), s))
-
-def hexdump2(src, length = 16): # dumps to a "hex editor" style output
- result = []
- for i in xrange(0, len(src), length):
- s = src[i:i + length]
- if(len(s) % 4 == 0):
- mod = 0
- else:
- mod = 1
- hexa = ''
- for j in range((len(s) / 4) + mod):
- hexa += ' '.join(["%02X" % ord(x) for x in s[j * 4:j * 4 + 4]])
- if(j != ((len(s) / 4) + mod) - 1):
- hexa += ' '
- printable = s.translate(''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)]))
- result.append("0x%04X %-*s %s\n" % (i, (length * 3) + 2, hexa, printable))
- return ''.join(result)
diff --git a/Puzzle+/license.txt b/Puzzle+/license.txt
deleted file mode 100755
index 63e41a4..0000000
--- a/Puzzle+/license.txt
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program 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 2 of the License, or
- (at your option) any later version.
-
- This program 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 this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. \ No newline at end of file
diff --git a/Puzzle+/nsmblib-0.5a.zip b/Puzzle+/nsmblib-0.5a.zip
deleted file mode 100644
index bca75e2..0000000
--- a/Puzzle+/nsmblib-0.5a.zip
+++ /dev/null
Binary files differ
diff --git a/Puzzle+/puzzle+.py b/Puzzle+/puzzle+.py
deleted file mode 100755
index 886a80a..0000000
--- a/Puzzle+/puzzle+.py
+++ /dev/null
@@ -1,1858 +0,0 @@
-#!/usr/bin/env python
-
-import archive
-import os.path
-import struct
-import sys
-import cPickle
-
-from ctypes import create_string_buffer
-from PyQt4 import QtCore, QtGui
-
-
-try:
- import nsmblib
- HaveNSMBLib = True
-except ImportError:
- HaveNSMBLib = False
-
-
-########################################################
-# To Do:
-#
-# - Object Editor
-# - Moving objects around
-#
-# - Make UI simpler for Pop
-# - C speed saving
-#
-########################################################
-
-
-Tileset = None
-
-#############################################################################################
-########################## Tileset Class and Tile/Object Subclasses #########################
-
-class TilesetClass():
- '''Contains Tileset data. Inits itself to a blank tileset.
- Methods: addTile, removeTile, addObject, removeObject, clear'''
-
- class Tile():
- def __init__(self, image, noalpha):
- '''Tile Constructor'''
-
- self.image = image
- self.noalpha = noalpha
-
-
- class Object():
-
- def __init__(self, height, width, uslope, lslope, tilelist):
- '''Tile Constructor'''
-
- self.height = height
- self.width = width
-
- self.upperslope = uslope
- self.lowerslope = lslope
-
- self.tiles = tilelist
-
-
- def __init__(self):
- '''Constructor'''
-
- self.tiles = []
- self.objects = []
-
- self.slot = 0
-
-
- def addTile(self, image, noalpha):
- '''Adds an tile class to the tile list with the passed image or parameters'''
-
- self.tiles.append(self.Tile(image, noalpha))
-
-
- def addObject(self, height = 1, width = 1, uslope = [0, 0], lslope = [0, 0], tilelist = [[(0, 0xFFFF, 0)]]):
- '''Adds a new object'''
-
- global Tileset
-
-
- # Initialize trusim power! This is required to work, due to python's single default parameter initialization
- if tilelist == [[(0, 0xFFFF, 0)]]:
- tilelist = [[(0, 0xFFFF, 0)]]
-
- self.objects.append(self.Object(height, width, uslope, lslope, tilelist))
-
-
- def removeObject(self, index):
- '''Removes an Object by Index number. Don't use this much, because we want objects to preserve their ID.'''
-
- self.objects.pop(index)
-
-
- def clear(self):
- '''Clears the tileset for a new file'''
-
- self.tiles = []
- self.objects = []
-
-
- def clearObjects(self):
- '''Clears the object data'''
-
- self.objects = []
-
-
-#############################################################################################
-##################### Object List Widget and Model Setup with Painter #######################
-
-
-class objectList(QtGui.QListView):
-
- def __init__(self, parent=None):
- super(objectList, self).__init__(parent)
-
-
- self.setFlow(QtGui.QListView.TopToBottom)
- # self.setViewMode(QtGui.QListView.ListMode)
- self.setIconSize(QtCore.QSize(96,96))
- self.setGridSize(QtCore.QSize(100,100))
- self.setMovement(QtGui.QListView.Static)
- self.setBackgroundRole(QtGui.QPalette.BrightText)
- self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
- self.setWrapping(False)
-
-
-
-def SetupObjectModel(self, objects, tiles):
- global Tileset
- self.clear()
-
- count = 0
- for object in objects:
- tex = QtGui.QPixmap(object.width * 24, object.height * 24)
- tex.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(tex)
-
- Xoffset = 0
- Yoffset = 0
-
- for i in range(len(object.tiles)):
- for tile in object.tiles[i]:
- if (Tileset.slot == 0):
- painter.drawPixmap(Xoffset, Yoffset, tiles[tile[1]].image)
- Xoffset += 24
- Xoffset = 0
- Yoffset += 24
-
- painter.end()
-
- self.appendRow(QtGui.QStandardItem(QtGui.QIcon(tex), 'Object {0}'.format(count)))
-
- count += 1
-
-
-@QtCore.pyqtSlot(QtGui.QTreeWidgetItem, int)
-def connectToTileWidget(tree, column):
-
- row = tree.text(0)
- if row[:7] == "Object ":
-
- newrow = int(row[7:])
- index = window.objmodel.index(newrow, 0)
-
- window.objectList.setCurrentIndex(index)
-
- window.tileWidget.setObject(index)
-
-
-
-#############################################################################################
-######################## List Widget with custom painter/MouseEvent #########################
-
-
-class displayWidget(QtGui.QListView):
-
- def __init__(self, parent=None):
- super(displayWidget, self).__init__(parent)
-
- self.setMinimumWidth(818)
- self.setMaximumWidth(818)
- self.setMinimumHeight(404)
- self.setMaximumHeight(404)
- self.setDragEnabled(True)
- self.setViewMode(QtGui.QListView.IconMode)
- self.setIconSize(QtCore.QSize(24,24))
- self.setGridSize(QtCore.QSize(25,25))
- self.setMovement(QtGui.QListView.Static)
- self.setAcceptDrops(False)
- self.setDropIndicatorShown(True)
- self.setResizeMode(QtGui.QListView.Adjust)
- self.setUniformItemSizes(True)
- self.setBackgroundRole(QtGui.QPalette.BrightText)
- self.setMouseTracking(True)
- self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
-
- self.setItemDelegate(self.TileItemDelegate())
-
-
-
- class TileItemDelegate(QtGui.QAbstractItemDelegate):
- """Handles tiles and their rendering"""
-
- def __init__(self):
- """Initialises the delegate"""
- QtGui.QAbstractItemDelegate.__init__(self)
-
- def paint(self, painter, option, index):
- """Paints an object"""
-
- global Tileset
- p = index.model().data(index, QtCore.Qt.DecorationRole)
- painter.drawPixmap(option.rect.x(), option.rect.y(), p.pixmap(24,24))
-
- x = option.rect.x()
- y = option.rect.y()
-
-
- # Collision Overlays
- curTile = Tileset.tiles[index.row()]
-
-
- # Highlight stuff.
- colour = QtGui.QColor(option.palette.highlight())
- colour.setAlpha(80)
-
- if option.state & QtGui.QStyle.State_Selected:
- painter.fillRect(option.rect, colour)
-
-
- def sizeHint(self, option, index):
- """Returns the size for the object"""
- return QtCore.QSize(24,24)
-
-
-
-#############################################################################################
-############################ Tile widget for drag n'drop Objects ############################
-
-
-class tileOverlord(QtGui.QWidget):
-
- def __init__(self):
- super(tileOverlord, self).__init__()
-
- # Setup Widgets
- self.tiles = tileWidget()
-
- self.addObject = QtGui.QPushButton('Add')
- self.removeObject = QtGui.QPushButton('Remove')
-
- self.addRow = QtGui.QPushButton('+')
- self.removeRow = QtGui.QPushButton('-')
-
- self.addColumn = QtGui.QPushButton('+')
- self.removeColumn = QtGui.QPushButton('-')
-
- self.tilingMethod = QtGui.QComboBox()
-
- self.tilingMethod.addItems(['Repeat',
- 'Stretch Center',
- 'Stretch X',
- 'Stretch Y',
- 'Repeat Bottom',
- 'Repeat Top',
- 'Repeat Left',
- 'Repeat Right',
- 'Upward slope',
- 'Downward slope',
- 'Downward reverse slope',
- 'Upward reverse slope'])
-
-
- # Connections
- self.addObject.released.connect(self.addObj)
- self.removeObject.released.connect(self.removeObj)
- self.addRow.released.connect(self.tiles.addRow)
- self.removeRow.released.connect(self.tiles.removeRow)
- self.addColumn.released.connect(self.tiles.addColumn)
- self.removeColumn.released.connect(self.tiles.removeColumn)
-
- self.tilingMethod.activated.connect(self.setTiling)
-
-
- # Layout
- layout = QtGui.QGridLayout()
-
- layout.addWidget(self.tilingMethod, 0, 0, 1, 3)
-
- layout.addWidget(self.addObject, 0, 6, 1, 1)
- layout.addWidget(self.removeObject, 0, 7, 1, 1)
-
- layout.setRowMinimumHeight(1, 40)
-
- layout.setRowStretch(1, 1)
- layout.setRowStretch(2, 5)
- layout.setRowStretch(5, 5)
- layout.addWidget(self.tiles, 2, 1, 4, 6)
-
- layout.addWidget(self.addColumn, 3, 7, 1, 1)
- layout.addWidget(self.removeColumn, 4, 7, 1, 1)
- layout.addWidget(self.addRow, 6, 3, 1, 1)
- layout.addWidget(self.removeRow, 6, 4, 1, 1)
-
- self.setLayout(layout)
-
-
- def addObj(self):
- global Tileset
-
- Tileset.addObject()
-
- pix = QtGui.QPixmap(24, 24)
- pix.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(pix)
- painter.drawPixmap(0, 0, pix)
- painter.end()
-
- count = len(Tileset.objects)
- window.objmodel.appendRow(QtGui.QStandardItem(QtGui.QIcon(pix), 'Object {0}'.format(count-1)))
- a = QtGui.QTreeWidgetItem(window.treeki)
- a.setText(0, 'Object {0}'.format(count-1))
- a.setFlags(QtCore.Qt.ItemFlags(0x25))
- a.setIcon(1, QtGui.QIcon(pix))
-
- index = window.objectList.currentIndex()
- window.objectList.setCurrentIndex(index)
- self.setObject(index)
-
- window.objectList.update()
- self.update()
-
-
- def removeObj(self):
- global Tileset
-
- index = window.objectList.currentIndex()
-
- Tileset.removeObject(index.row())
- window.objmodel.removeRow(index.row())
- self.tiles.clear()
-
- matchList = window.treeki.findItems("Object {0}".format(index.row()), QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive | QtCore.Qt.MatchWrap)
- for x in matchList:
- index = window.treeki.indexFromItem(x, 0)
- realx = index.row()
- if x.parent():
- y = x.parent().takeChild(realx)
- del y
- else:
- y =window.treeki.takeTopLevelItem(realx)
- del y
-
-
- window.objectList.update()
- self.update()
-
-
- def setObject(self, index):
- global Tileset
- object = Tileset.objects[index.row()]
-
- width = len(object.tiles[0])-1
- height = len(object.tiles)-1
- Xuniform = True
- Yuniform = True
- Xstretch = False
- Ystretch = False
-
- for tile in object.tiles[0]:
- if tile[0] != object.tiles[0][0][1]:
- Xuniform = False
-
- for tile in object.tiles:
- if tile[0][0] != object.tiles[0][0][1]:
- Yuniform = False
-
- if object.tiles[0][0][1] == object.tiles[0][width][0] and Xuniform == False:
- Xstretch = True
-
- if object.tiles[0][0][1] == object.tiles[height][0][0] and Xuniform == False:
- Ystretch = True
-
-
-
- if object.upperslope[0] != 0:
- if object.upperslope[0] == 0x90:
- self.tilingMethod.setCurrentIndex(8)
- elif object.upperslope[0] == 0x91:
- self.tilingMethod.setCurrentIndex(9)
- elif object.upperslope[0] == 0x92:
- self.tilingMethod.setCurrentIndex(10)
- elif object.upperslope[0] == 0x93:
- self.tilingMethod.setCurrentIndex(11)
-
- else:
- if Xuniform and Yuniform:
- self.tilingMethod.setCurrentIndex(0)
- elif Xstretch and Ystretch:
- self.tilingMethod.setCurrentIndex(1)
- elif Xstretch:
- self.tilingMethod.setCurrentIndex(2)
- elif Ystretch:
- self.tilingMethod.setCurrentIndex(3)
- elif Xuniform and Yuniform == False and object.tiles[0][0][0] == 0:
- self.tilingMethod.setCurrentIndex(4)
- elif Xuniform and Yuniform == False and object.tiles[height][0][0] == 0:
- self.tilingMethod.setCurrentIndex(5)
- elif Xuniform == False and Yuniform and object.tiles[0][0][0] == 0:
- self.tilingMethod.setCurrentIndex(6)
- elif Xuniform == False and Yuniform and object.tiles[0][width][0] == 0:
- self.tilingMethod.setCurrentIndex(7)
-
-
- self.tiles.setObject(object)
-
- # print 'Object {0}, Width: {1} / Height: {2}, Slope {3}/{4}'.format(index.row(), object.width, object.height, object.upperslope, object.lowerslope)
- # for row in object.tiles:
- # print 'Row: {0}'.format(row)
- # print ''
-
- @QtCore.pyqtSlot(int)
- def setTiling(self, listindex):
- global Tileset
-
- index = window.objectList.currentIndex()
- object = Tileset.objects[index.row()]
-
-
- if listindex == 0: # Repeat
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- if listindex == 1: # Stretch Center
-
- if object.width < 3 and object.height < 3:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 3 tiles\nwide and 3 tiles tall to apply stretch center.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if crow == 0 and ctile == 0:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif crow == 0 and ctile == object.width-1:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif crow == object.height-1 and ctile == object.width-1:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif crow == object.height-1 and ctile == 0:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif crow == 0 or crow == object.height-1:
- object.tiles[crow][ctile] = (1, tile[1], tile[2])
- elif ctile == 0 or ctile == object.width-1:
- object.tiles[crow][ctile] = (2, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (3, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 2: # Stretch X
-
- if object.width < 3:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 3 tiles\nwide to apply stretch X.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if ctile == 0:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif ctile == object.width-1:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (1, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 3: # Stretch Y
-
- if object.height < 3:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 3 tiles\ntall to apply stretch Y.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if crow == 0:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- elif crow == object.height-1:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (2, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 4: # Repeat Bottom
-
- if object.height < 2:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 2 tiles\ntall to apply repeat bottom.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if crow == object.height-1:
- object.tiles[crow][ctile] = (2, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 5: # Repeat Top
-
- if object.height < 2:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 2 tiles\ntall to apply repeat top.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if crow == 0:
- object.tiles[crow][ctile] = (2, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 6: # Repeat Left
-
- if object.width < 2:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 2 tiles\nwide to apply repeat left.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if ctile == 0:
- object.tiles[crow][ctile] = (1, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
- if listindex == 7: # Repeat Right
-
- if object.width < 2:
- reply = QtGui.QMessageBox.information(self, "Warning", "An object must be at least 2 tiles\nwide to apply repeat right.")
- self.setObject(index)
- return
-
- ctile = 0
- crow = 0
-
- for row in object.tiles:
- for tile in row:
- if ctile == object.width-1:
- object.tiles[crow][ctile] = (1, tile[1], tile[2])
- else:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0, 0]
- object.lowerslope = [0, 0]
-
-
- if listindex == 8: # Upward Slope
- ctile = 0
- crow = 0
- for row in object.tiles:
- for tile in row:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0x90, 1]
- object.lowerslope = [0x84, object.height - 1]
- self.tiles.slope = 1
-
- self.tiles.update()
-
- if listindex == 9: # Downward Slope
- ctile = 0
- crow = 0
- for row in object.tiles:
- for tile in row:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0x91, 1]
- object.lowerslope = [0x84, object.height - 1]
- self.tiles.slope = 1
-
- self.tiles.update()
-
- if listindex == 10: # Upward Reverse Slope
- ctile = 0
- crow = 0
- for row in object.tiles:
- for tile in row:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0x92, object.height - 1]
- object.lowerslope = [0x84, 1]
- self.tiles.slope = 0-(object.height-1)
-
- self.tiles.update()
-
- if listindex == 11: # Downward Reverse Slope
- ctile = 0
- crow = 0
- for row in object.tiles:
- for tile in row:
- object.tiles[crow][ctile] = (0, tile[1], tile[2])
- ctile += 1
- crow += 1
- ctile = 0
-
- object.upperslope = [0x93, object.height - 1]
- object.lowerslope = [0x84, 1]
- self.tiles.slope = 0-(object.height-1)
-
- self.tiles.update()
-
-
-class tileWidget(QtGui.QWidget):
-
- def __init__(self):
- super(tileWidget, self).__init__()
-
- self.tiles = []
-
- self.size = [1, 1]
- self.setMinimumSize(120, 120)
-
- self.slope = 0
-
- self.highlightedRect = QtCore.QRect()
-
- self.setAcceptDrops(True)
- self.object = 0
-
-
- def clear(self):
- self.tiles = []
- self.size = [1, 1] # [width, height]
-
- self.slope = 0
- self.highlightedRect = QtCore.QRect()
-
- self.update()
-
- return
-
-
- def addColumn(self):
- global Tileset
-
- if self.size[0] >= 24:
- return
-
- if len(Tileset.objects) == 0:
- window.tileWidget.addObj()
-
- self.size[0] += 1
- self.setMinimumSize(self.size[0]*24, self.size[1]*24)
-
- pix = QtGui.QPixmap(24,24)
- pix.fill(QtGui.QColor(205, 205, 255))
-
- for y in xrange(self.size[1]):
- self.tiles.insert(((y+1) * self.size[0]) -1, [self.size[0]-1, y, pix])
-
-
- curObj = Tileset.objects[self.object]
- curObj.width += 1
-
- for row in curObj.tiles:
- row.append((0, 0xFFFF, 0))
-
- self.update()
- self.updateList()
-
-
- def removeColumn(self):
- global Tileset
-
- if self.size[0] == 1:
- return
-
- if len(Tileset.objects) == 0:
- window.tileWidget.addObj()
-
- for y in xrange(self.size[1]):
- self.tiles.pop(((y+1) * self.size[0])-(y+1))
-
- self.size[0] = self.size[0] - 1
- self.setMinimumSize(self.size[0]*24, self.size[1]*24)
-
-
- curObj = Tileset.objects[self.object]
- curObj.width -= 1
-
- for row in curObj.tiles:
- row.pop()
-
- self.update()
- self.updateList()
-
-
- def addRow(self):
- global Tileset
-
- if len(Tileset.objects) == 0:
- window.tileWidget.addObj()
-
- if self.size[1] >= 24:
- return
-
- self.size[1] += 1
- self.setMinimumSize(self.size[0]*24, self.size[1]*24)
-
- pix = QtGui.QPixmap(24,24)
- pix.fill(QtGui.QColor(205, 205, 255))
-
- for x in xrange(self.size[0]):
- self.tiles.append([x, self.size[1]-1, pix])
-
- curObj = Tileset.objects[self.object]
- curObj.height += 1
-
- curObj.tiles.append([])
- for i in xrange(0, curObj.width):
- curObj.tiles[len(curObj.tiles)-1].append((0, 0xFFFF, 0))
-
- self.update()
- self.updateList()
-
-
- def removeRow(self):
- global Tileset
-
- if self.size[1] == 1:
- return
-
- if len(Tileset.objects) == 0:
- window.tileWidget.addObj()
-
- for x in xrange(self.size[0]):
- self.tiles.pop()
-
- self.size[1] -= 1
- self.setMinimumSize(self.size[0]*24, self.size[1]*24)
-
- curObj = Tileset.objects[self.object]
- curObj.height -= 1
-
- curObj.tiles.pop()
-
- self.update()
- self.updateList()
-
-
- def setObject(self, object):
- self.clear()
-
- global Tileset
-
- self.size = [object.width, object.height]
-
- if not object.upperslope[1] == 0:
- if object.upperslope[0] & 2:
- self.slope = 0 - object.lowerslope[1]
- else:
- self.slope = object.upperslope[1]
-
- x = 0
- y = 0
- for row in object.tiles:
- for tile in row:
-
- if (Tileset.slot == 0) or ((tile[2] & 3) != 0):
- if (tile[1] == 0xFFFF):
- pix = QtGui.QPixmap(24,24)
- pix.fill(QtGui.QColor(205, 205, 255))
- self.tiles.append([x, y, pix])
- else:
- self.tiles.append([x, y, Tileset.tiles[tile[1]].image])
- else:
- pix = QtGui.QPixmap(24,24)
- pix.fill(QtGui.QColor(205, 205, 255))
- self.tiles.append([x, y, pix])
- x += 1
- y += 1
- x = 0
-
-
- self.object = window.objectList.currentIndex().row()
- self.update()
- self.updateList()
-
-
- def contextMenuEvent(self, event):
-
- TileMenu = QtGui.QMenu(self)
- self.contX = event.x()
- self.contY = event.y()
-
- TileMenu.addAction('Set tile...', self.setTile)
-
- TileMenu.exec_(event.globalPos())
-
-
- def mousePressEvent(self, event):
- global Tileset
-
- if event.button() == 2:
- return
-
- if window.tileDisplay.selectedIndexes() == []:
- return
-
- currentSelected = window.tileDisplay.selectedIndexes()
-
- ix = 0
- iy = 0
- for modelItem in currentSelected:
- # Update yourself!
- centerPoint = self.contentsRect().center()
-
- tile = modelItem.row()
- upperLeftX = centerPoint.x() - self.size[0]*12
- upperLeftY = centerPoint.y() - self.size[1]*12
-
- lowerRightX = centerPoint.x() + self.size[0]*12
- lowerRightY = centerPoint.y() + self.size[1]*12
-
-
- x = (event.x() - upperLeftX)/24 + ix
- y = (event.y() - upperLeftY)/24 + iy
-
- if event.x() < upperLeftX or event.y() < upperLeftY or event.x() > lowerRightX or event.y() > lowerRightY:
- return
-
- self.tiles[(y * self.size[0]) + x][2] = Tileset.tiles[tile].image
-
- Tileset.objects[self.object].tiles[y][x] = (Tileset.objects[self.object].tiles[y][x][0], tile, Tileset.slot)
-
- ix += 1
- if self.size[0]-1 < ix:
- ix = 0
- iy += 1
- if iy > self.size[1]-1:
- break
-
-
- self.update()
-
- self.updateList()
-
-
- def updateList(self):
- # Update the list >.>
- object = window.objmodel.itemFromIndex(window.objectList.currentIndex())
- matchList = window.treeki.findItems("Object {0}".format(window.objectList.currentIndex().row()), QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive | QtCore.Qt.MatchWrap)
-
-
- tex = QtGui.QPixmap(self.size[0] * 24, self.size[1] * 24)
- tex.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(tex)
-
- Xoffset = 0
- Yoffset = 0
-
- for tile in self.tiles:
- painter.drawPixmap(tile[0]*24, tile[1]*24, tile[2])
-
- painter.end()
-
- try:
- object.setIcon(QtGui.QIcon(tex))
- matchList[0].setIcon(1, QtGui.QIcon(tex))
- except:
- pass
-
- window.objectList.update()
-
-
-
- def setTile(self):
- global Tileset
-
- dlg = self.setTileDialog()
- if dlg.exec_() == QtGui.QDialog.Accepted:
- # Do stuff
- centerPoint = self.contentsRect().center()
-
- upperLeftX = centerPoint.x() - self.size[0]*12
- upperLeftY = centerPoint.y() - self.size[1]*12
-
- tile = dlg.tile.value()
- tileset = dlg.tileset.currentIndex()
-
- x = (self.contX - upperLeftX)/24
- y = (self.contY - upperLeftY)/24
-
- if tileset != Tileset.slot:
- tex = QtGui.QPixmap(self.size[0] * 24, self.size[1] * 24)
- tex.fill(QtCore.Qt.transparent)
-
- self.tiles[(y * self.size[0]) + x][2] = tex
-
- Tileset.objects[self.object].tiles[y][x] = (Tileset.objects[self.object].tiles[y][x][0], tile, tileset)
-
- self.update()
- self.updateList()
-
-
- class setTileDialog(QtGui.QDialog):
-
- def __init__(self):
- QtGui.QDialog.__init__(self)
-
- self.setWindowTitle('Set tiles')
-
- self.tile = QtGui.QSpinBox()
- self.tile.setRange(0, 512)
-
- self.buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
- self.buttons.accepted.connect(self.accept)
- self.buttons.rejected.connect(self.reject)
-
- self.layout = QtGui.QGridLayout()
- self.layout.addWidget(QtGui.QLabel('Tile:'), 0,3,1,1, QtCore.Qt.AlignLeft)
- self.layout.addWidget(self.tile, 1, 3, 1, 3)
- self.layout.addWidget(self.buttons, 2, 3)
- self.setLayout(self.layout)
-
-
-
- def paintEvent(self, event):
- painter = QtGui.QPainter()
- painter.begin(self)
-
- centerPoint = self.contentsRect().center()
- upperLeftX = centerPoint.x() - self.size[0]*12
- lowerRightX = centerPoint.x() + self.size[0]*12
-
- upperLeftY = centerPoint.y() - self.size[1]*12
- lowerRightY = centerPoint.y() + self.size[1]*12
-
-
- painter.fillRect(upperLeftX, upperLeftY, self.size[0] * 24, self.size[1]*24, QtGui.QColor(205, 205, 255))
-
- for x, y, pix in self.tiles:
- painter.drawPixmap(upperLeftX + (x * 24), upperLeftY + (y * 24), pix)
-
- if not self.slope == 0:
- pen = QtGui.QPen()
- # pen.setStyle(QtCore.Qt.QDashLine)
- pen.setWidth(1)
- pen.setColor(QtCore.Qt.blue)
- painter.setPen(QtGui.QPen(pen))
- painter.drawLine(upperLeftX, upperLeftY + (abs(self.slope) * 24), lowerRightX, upperLeftY + (abs(self.slope) * 24))
-
- if self.slope > 0:
- main = 'Main'
- sub = 'Sub'
- elif self.slope < 0:
- main = 'Sub'
- sub = 'Main'
-
- font = painter.font()
- font.setPixelSize(8)
- font.setFamily('Monaco')
- painter.setFont(font)
-
- painter.drawText(upperLeftX+1, upperLeftY+10, main)
- painter.drawText(upperLeftX+1, upperLeftY + (abs(self.slope) * 24) + 9, sub)
-
- painter.end()
-
-
-
-#############################################################################################
-############################ Subclassed one dimension Item Model ############################
-
-
-class PiecesModel(QtCore.QAbstractListModel):
- def __init__(self, parent=None):
- super(PiecesModel, self).__init__(parent)
-
- self.pixmaps = []
- self.setSupportedDragActions(QtCore.Qt.CopyAction | QtCore.Qt.MoveAction | QtCore.Qt.LinkAction)
-
- def data(self, index, role=QtCore.Qt.DisplayRole):
- if not index.isValid():
- return None
-
- if role == QtCore.Qt.DecorationRole:
- return QtGui.QIcon(self.pixmaps[index.row()])
-
- if role == QtCore.Qt.UserRole:
- return self.pixmaps[index.row()]
-
- return None
-
- def addPieces(self, pixmap):
- row = len(self.pixmaps)
-
- self.beginInsertRows(QtCore.QModelIndex(), row, row)
- self.pixmaps.insert(row, pixmap)
- self.endInsertRows()
-
- def flags(self,index):
- if index.isValid():
- return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable |
- QtCore.Qt.ItemIsDragEnabled)
-
- def clear(self):
- row = len(self.pixmaps)
-
- del self.pixmaps[:]
-
-
- def mimeTypes(self):
- return ['image/x-tile-piece']
-
-
- def mimeData(self, indexes):
- mimeData = QtCore.QMimeData()
- encodedData = QtCore.QByteArray()
-
- stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.WriteOnly)
-
- for index in indexes:
- if index.isValid():
- pixmap = QtGui.QPixmap(self.data(index, QtCore.Qt.UserRole))
- stream << pixmap
-
- mimeData.setData('image/x-tile-piece', encodedData)
- return mimeData
-
-
- def rowCount(self, parent):
- if parent.isValid():
- return 0
- else:
- return len(self.pixmaps)
-
- def supportedDragActions(self):
- return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
-
-
-
-#############################################################################################
-################## Python-based RGB5a3 Decoding code from my BRFNT program ##################
-
-
-def RGB4A3Decode(tex):
- dest = QtGui.QImage(1024,512,QtGui.QImage.Format_ARGB32)
- dest.fill(QtCore.Qt.transparent)
-
- i = 0
- for ytile in xrange(0, 512, 4):
- for xtile in xrange(0, 1024, 4):
- for ypixel in xrange(ytile, ytile + 4):
- for xpixel in xrange(xtile, xtile + 4):
-
- if(xpixel >= 1024 or ypixel >= 512):
- continue
-
- newpixel = (tex[i] << 8) | tex[i+1]
-
-
- if(newpixel >= 0x8000): # Check if it's RGB555
- red = ((newpixel >> 10) & 0x1F) * 255 / 0x1F
- green = ((newpixel >> 5) & 0x1F) * 255 / 0x1F
- blue = (newpixel & 0x1F) * 255 / 0x1F
- alpha = 0xFF
-
- else: # If not, it's RGB4A3
- alpha = ((newpixel & 0x7000) >> 12) * 255 / 0x7
- blue = ((newpixel & 0xF00) >> 8) * 255 / 0xF
- green = ((newpixel & 0xF0) >> 4) * 255 / 0xF
- red = (newpixel & 0xF) * 255 / 0xF
-
- argb = (blue) | (green << 8) | (red << 16) | (alpha << 24)
- dest.setPixel(xpixel, ypixel, argb)
- i += 2
- return dest
-
-
-def RGB4A3Encode(tex):
- destBuffer = create_string_buffer(1024*512*2)
-
- shortstruct = struct.Struct('>H')
- offset = 0
-
- for ytile in xrange(0, 512, 4):
- for xtile in xrange(0, 1024, 4):
- for ypixel in xrange(ytile, ytile + 4):
- for xpixel in xrange(xtile, xtile + 4):
-
- if(xpixel >= 1024 or ypixel >= 512):
- continue
-
- pixel = tex.pixel(xpixel, ypixel)
-
- a = pixel >> 24
- r = (pixel >> 16) & 0xFF
- g = (pixel >> 8) & 0xFF
- b = pixel & 0xFF
-
- if a < 245: #RGB4A3
- alpha = a/32
- red = r/16
- green = g/16
- blue = b/16
-
- rgbDAT = (blue) | (green << 4) | (red << 8) | (alpha << 12)
-
- else: # RGB555
- red = r/8
- green = g/8
- blue = b/8
-
- rgbDAT = (blue) | (green << 5) | (red << 10) | (0x8000) # 0rrrrrgggggbbbbb
-
- shortstruct.pack_into(destBuffer, offset, rgbDAT)
- offset += 2
-
- return destBuffer.raw
-
-
-#############################################################################################
-############ Main Window Class. Takes care of menu functions and widget creation ############
-
-
-class MainWindow(QtGui.QMainWindow):
- def __init__(self, parent=None):
- super(MainWindow, self).__init__(parent)
-
- self.tileImage = QtGui.QPixmap()
- self.alpha = True
-
- global Tileset
- Tileset = TilesetClass()
-
- self.name = ''
-
- self.setupMenus()
- self.setupWidgets()
-
- self.setuptile()
-
- self.newTileset()
-
- self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
- QtGui.QSizePolicy.Fixed))
- self.setWindowTitle("New Tileset")
-
-
- def setuptile(self):
- self.tileWidget.tiles.clear()
- self.model.clear()
-
- if self.alpha == True:
- for tile in Tileset.tiles:
- self.model.addPieces(tile.image)
- else:
- for tile in Tileset.tiles:
- self.model.addPieces(tile.noalpha)
-
-
- def newTileset(self):
- '''Creates a new, blank tileset'''
-
- global Tileset
- Tileset.clear()
- Tileset = TilesetClass()
-
- self.treeki.clear()
- # self.objectList.clear()
- self.objmodel.clear()
-
- EmptyPix = QtGui.QPixmap(24, 24)
- EmptyPix.fill(QtCore.Qt.black)
-
- for i in range(512):
- Tileset.addTile(EmptyPix, EmptyPix)
-
- self.setuptile()
- self.setWindowTitle('New Tileset')
-
-
- def openTileset(self):
- '''Opens a Koopatlas tileset arc and parses the heck out of it.'''
-
- path = str(QtGui.QFileDialog.getOpenFileName(self, "Open Koopatlas Tileset", '',
- "Image Files (*.arc)"))
-
- if path:
- self.setWindowTitle(os.path.basename(path))
- Tileset.clear()
-
- name = path[str(path).rfind('/')+1:-4]
-
- file = open(path,'rb')
- data = file.read()
- file.close()
-
- arc = archive.U8()
- arc._load(data)
-
- Image = None
- behaviourdata = None
- objstrings = None
- metadata = None
-
- for key, value in arc.files:
- if value == None:
- pass
- if key.startswith('BG_tex/') and key.endswith('_tex.bin'):
- Image = arc[key]
- if key.startswith('BG_grp/') and key.endswith('_grp.bin'):
- Image = arc[key]
- if key.startswith('BG_unt/'):
- if key.endswith('_hd.bin'):
- metadata = arc[key]
- elif key.endswith('.bin'):
- objstrings = arc[key]
-
-
- if (Image == None) or (group == None) or (objstrings == None) or (metadata == None):
- QtGui.QMessageBox.warning(None, 'Error', 'Error - the necessary files were not found.\n\nNot a valid Koopatlas tileset, sadly.')
- return
-
- # Stolen from Reggie! Loads the Image Data.
- if HaveNSMBLib:
- argbdata = nsmblib.decodeTileset(Image)
- rgbdata = nsmblib.decodeTilesetNoAlpha(Image)
- dest = QtGui.QImage(argbdata, 1024, 512, 4096, QtGui.QImage.Format_ARGB32_Premultiplied)
- noalphadest = QtGui.QImage(rgbdata, 1024, 512, 4096, QtGui.QImage.Format_ARGB32_Premultiplied)
- else:
- dest = RGB4A3Decode(Image)
-
- self.tileImage = QtGui.QPixmap.fromImage(dest)
- noalpha = QtGui.QPixmap.fromImage(noalphadest)
-
-
- # Makes us some nice Tile Classes!
- Xoffset = 4
- Yoffset = 4
- for i in range(512):
- Tileset.addTile(self.tileImage.copy(Xoffset,Yoffset,24,24), noalpha.copy(Xoffset,Yoffset,24,24))
- Xoffset += 32
- if Xoffset >= 1024:
- Xoffset = 4
- Yoffset += 32
-
-
- # Load Objects
-
- meta = []
- for i in xrange(len(metadata)/4):
- meta.append(struct.unpack_from('>H2B', metadata, i * 4))
-
- tilelist = [[]]
- upperslope = [0, 0]
- lowerslope = [0, 0]
- byte = 0
-
- for entry in meta:
- offset = entry[0]
- byte = struct.unpack_from('>B', objstrings, offset)[0]
- row = 0
-
- while byte != 0xFF:
-
- if byte == 0xFE:
- tilelist.append([])
-
- if (upperslope[0] != 0) and (lowerslope[0] == 0):
- upperslope[1] = upperslope[1] + 1
-
- if lowerslope[0] != 0:
- lowerslope[1] = lowerslope[1] + 1
-
- offset += 1
- byte = struct.unpack_from('>B', objstrings, offset)[0]
-
- elif (byte & 0x80):
-
- if upperslope[0] == 0:
- upperslope[0] = byte
- else:
- lowerslope[0] = byte
-
- offset += 1
- byte = struct.unpack_from('>B', objstrings, offset)[0]
-
- else:
- tilelist[len(tilelist)-1].append(struct.unpack_from('>BH', objstrings, offset).extend([0]))
-
- offset += 3
- byte = struct.unpack_from('>B', objstrings, offset)[0]
-
- tilelist.pop()
-
- if (upperslope[0] & 0x80) and (upperslope[0] & 0x2):
- for i in range(lowerslope[1]):
- pop = tilelist.pop()
- tilelist.insert(0, pop)
-
- Tileset.addObject(entry[2], entry[1], upperslope, lowerslope, tilelist)
-
- tilelist = [[]]
- upperslope = [0, 0]
- lowerslope = [0, 0]
-
- Tileset.slot = Tileset.objects[0].tiles[0][0][2] & 3
- self.tileWidget.tilesetType.setText('Pa{0}'.format(Tileset.slot))
-
- self.setuptile()
- SetupObjectModel(self.objmodel, Tileset.objects, Tileset.tiles)
-
- self.name = path
-
-
- def openImage(self):
- '''Opens an Image from png, and creates a new tileset from it.'''
-
- path = QtGui.QFileDialog.getOpenFileName(self, "Open Image", '',
- "Image Files (*.png)")
-
- if path:
- newImage = QtGui.QPixmap()
- self.tileImage = newImage
-
- if not newImage.load(path):
- QtGui.QMessageBox.warning(self, "Open Image",
- "The image file could not be loaded.",
- QtGui.QMessageBox.Cancel)
- return
-
- if ((newImage.width() == 768) & (newImage.height() == 384)):
- x = 0
- y = 0
- for i in range(512):
- Tileset.tiles[i].image = self.tileImage.copy(x*24,y*24,24,24)
- x += 1
- if (x * 24) >= 768:
- y += 1
- x = 0
-
- else:
- QtGui.QMessageBox.warning(self, "Open Image",
- "The image was not the proper dimensions."
- "Please resize the image to 768x384 pixels.",
- QtGui.QMessageBox.Cancel)
- return
-
-
- self.setuptile()
-
-
- def saveImage(self):
-
- fn = QtGui.QFileDialog.getSaveFileName(self, 'Choose a new filename', '', '.png (*.png)')
- if fn == '': return
-
- tex = QtGui.QPixmap(768, 384)
- tex.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(tex)
-
- Xoffset = 0
- Yoffset = 0
-
- for tile in Tileset.tiles:
- painter.drawPixmap(Xoffset, Yoffset, tile.image)
- Xoffset += 24
- if Xoffset >= 768:
- Xoffset = 0
- Yoffset += 24
-
- painter.end()
-
- tex.save(fn)
-
-
- def saveTileset(self):
- if self.name == '':
- self.saveTilesetAs()
- return
-
-
- outdata = self.saving(os.path.basename(self.name)[:-4])
-
- fn = self.name
- f = open(fn, 'wb')
- f.write(outdata)
- f.close()
-
-
- def saveTilesetAs(self):
-
- fn = QtGui.QFileDialog.getSaveFileName(self, 'Choose a new filename', '', '.arc (*.arc)')
- if fn == '': return
-
- self.name = fn
- self.setWindowTitle(os.path.basename(unicode(fn)))
-
- outdata = self.saving(os.path.basename(unicode(fn))[:-4])
- f = open(fn, 'wb')
- f.write(outdata)
- f.close()
-
-
- def saving(self, name):
-
- # Prepare tiles, objects, object metadata, and textures and stuff into buffers.
-
- textureBuffer = self.PackTexture()
- objectBuffers = self.PackObjects()
- objectBuffer = objectBuffers[0]
- objectMetaBuffer = objectBuffers[1]
- groupBuffer = self.PackGroups()
-
-
- # Make an arc and pack up the files!
- arc = archive.U8()
- arc['BG_tex'] = None
- arc['BG_tex/{0}_tex.bin'.format(name)] = textureBuffer
-
- arc['BG_unt'] = None
- arc['BG_unt/{0}.bin'.format(name)] = objectBuffer
- arc['BG_unt/{0}_hd.bin'.format(name)] = objectMetaBuffer
-
- arc['BG_grp'] = None
- arc['BG_grp/{0}_grp.bin'.format(name)] = groupBuffer
-
- return arc._dump()
-
-
- def PackTexture(self):
-
- tex = QtGui.QImage(1024, 512, QtGui.QImage.Format_ARGB32)
- tex.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(tex)
-
- Xoffset = 0
- Yoffset = 0
-
- for tile in Tileset.tiles:
- minitex = QtGui.QImage(32, 32, QtGui.QImage.Format_ARGB32)
- minitex.fill(QtCore.Qt.transparent)
- minipainter = QtGui.QPainter(minitex)
-
- minipainter.drawPixmap(4, 4, tile.image)
- minipainter.end()
-
- # Read colours and DESTROY THEM (or copy them to the edges, w/e)
- for i in xrange(4,28):
-
- # Top Clamp
- colour = minitex.pixel(i, 4)
- for p in xrange(0,4):
- minitex.setPixel(i, p, colour)
-
- # Left Clamp
- colour = minitex.pixel(4, i)
- for p in xrange(0,4):
- minitex.setPixel(p, i, colour)
-
- # Right Clamp
- colour = minitex.pixel(i, 27)
- for p in xrange(27,31):
- minitex.setPixel(i, p, colour)
-
- # Bottom Clamp
- colour = minitex.pixel(27, i)
- for p in xrange(27,31):
- minitex.setPixel(p, i, colour)
-
- # UpperLeft Corner Clamp
- colour = minitex.pixel(4, 4)
- for x in xrange(0,4):
- for y in xrange(0,4):
- minitex.setPixel(x, y, colour)
-
- # UpperRight Corner Clamp
- colour = minitex.pixel(27, 4)
- for x in xrange(27,31):
- for y in xrange(0,4):
- minitex.setPixel(x, y, colour)
-
- # LowerLeft Corner Clamp
- colour = minitex.pixel(4, 27)
- for x in xrange(0,4):
- for y in xrange(27,31):
- minitex.setPixel(x, y, colour)
-
- # LowerRight Corner Clamp
- colour = minitex.pixel(27, 27)
- for x in xrange(27,31):
- for y in xrange(27,31):
- minitex.setPixel(x, y, colour)
-
-
- painter.drawImage(Xoffset, Yoffset, minitex)
-
- Xoffset += 32
-
- if Xoffset >= 1024:
- Xoffset = 0
- Yoffset += 32
-
- painter.end()
-
- dest = RGB4A3Encode(tex)
-
- return dest
-
-
-
- def PackObjects(self):
- objectStrings = []
-
- o = 0
- for object in Tileset.objects:
-
-
- # Slopes
- if object.upperslope[0] != 0:
-
- # Reverse Slopes
- if object.upperslope[0] & 0x2:
- a = struct.pack('>B', object.upperslope[0])
-
- if object.height == 1:
- iterationsA = 0
- iterationsB = 1
- else:
- iterationsA = object.upperslope[1]
- iterationsB = object.lowerslope[1] + object.upperslope[1]
-
- for row in xrange(iterationsA, iterationsB):
- for tile in object.tiles[row]:
- a = a + struct.pack('>BH', tile[0], tile[1])
- a = a + '\xfe'
-
- if object.height > 1:
- a = a + struct.pack('>B', object.lowerslope[0])
-
- for row in xrange(0, object.upperslope[1]):
- for tile in object.tiles[row]:
- a = a + struct.pack('>BH', tile[0], tile[1])
- a = a + '\xfe'
-
- a = a + '\xff'
-
- objectStrings.append(a)
-
-
- # Regular Slopes
- else:
- a = struct.pack('>B', object.upperslope[0])
-
- for row in xrange(0, object.upperslope[1]):
- for tile in object.tiles[row]:
- a = a + struct.pack('>BH', tile[0], tile[1])
- a = a + '\xfe'
-
- if object.height > 1:
- a = a + struct.pack('>B', object.lowerslope[0])
-
- for row in xrange(object.upperslope[1], object.height):
- for tile in object.tiles[row]:
- a = a + struct.pack('>BH', tile[0], tile[1])
- a = a + '\xfe'
-
- a = a + '\xff'
-
- objectStrings.append(a)
-
-
- # Not slopes!
- else:
- a = ''
-
- for tilerow in object.tiles:
- for tile in tilerow:
- a = a + struct.pack('>BH', tile[0], tile[1])
-
- a = a + '\xfe'
-
- a = a + '\xff'
-
- objectStrings.append(a)
-
- o += 1
-
- Objbuffer = ''
- Metabuffer = ''
- i = 0
- for a in objectStrings:
- Metabuffer = Metabuffer + struct.pack('>H2B', len(Objbuffer), Tileset.objects[i].width, Tileset.objects[i].height)
- Objbuffer = Objbuffer + a
-
- i += 1
-
- return (Objbuffer, Metabuffer)
-
-
-
- def PackGroups(self):
-
- groupList = []
- for kiddy in xrange(self.treeki.topLevelItemCount()):
- self.walkTree(groupList, self.treeki.topLevelItem(kiddy));
-
- return cPickle.dumps(groupList)
-
-
- def walkTree(self, stringsList, treeItem):
-
- for kiddy in xrange(treeItem.childCount()):
- newList = []
- self.walkTree(newList, treeItem.child(kiddy))
-
- if treeItem.childCount() > 0:
- stringsList.append(str(treeItem.text(0)), newList)
- else:
- stringsList.append(str(treeItem.text(0)))
-
-
- def setupMenus(self):
- fileMenu = self.menuBar().addMenu("&File")
-
- pixmap = QtGui.QPixmap(60,60)
- pixmap.fill(QtCore.Qt.black)
- icon = QtGui.QIcon(pixmap)
-
- self.action = fileMenu.addAction(icon, "New", self.newTileset, QtGui.QKeySequence.New)
- fileMenu.addAction("Open...", self.openTileset, QtGui.QKeySequence.Open)
- fileMenu.addAction("Import Image...", self.openImage, QtGui.QKeySequence('Ctrl+I'))
- fileMenu.addAction("Export Image...", self.saveImage, QtGui.QKeySequence('Ctrl+E'))
- fileMenu.addAction("Save", self.saveTileset, QtGui.QKeySequence.Save)
- fileMenu.addAction("Save as...", self.saveTilesetAs, QtGui.QKeySequence.SaveAs)
- fileMenu.addAction("Quit", self.close, QtGui.QKeySequence('Ctrl-Q'))
-
- taskMenu = self.menuBar().addMenu("&Tasks")
-
- taskMenu.addAction("Toggle Alpha", self.toggleAlpha, QtGui.QKeySequence('Ctrl+Shift+A'))
- taskMenu.addAction("Clear Object Data", Tileset.clearObjects, QtGui.QKeySequence('Ctrl+Alt+Backspace'))
-
-
-
- def toggleAlpha(self):
- # Replace Alpha Image with non-Alpha images in model
- if self.alpha == True:
- self.alpha = False
- else:
- self.alpha = True
-
- self.setuptile()
-
-
- def TriggerNewGroup(self):
-
- a = QtGui.QTreeWidgetItem(self.treeki)
- a.setText(0, 'Double Click to Rename')
- a.setFlags(QtCore.Qt.ItemFlags(0x2F))
- a.setChildIndicatorPolicy(QtGui.QTreeWidgetItem.ShowIndicator)
- a.setExpanded(True)
-
-
- def TriggerDelGroup(self):
-
- treecko = self.treeki.currentItem()
- if str(treecko.text(0)).find("Object"):
- eggs = treecko.takeChildren()
-
- self.treeki.addTopLevelItems(eggs)
-
- index = window.treeki.indexFromItem(treecko, 0)
- realx = index.row()
-
- if treecko.parent():
- y = treecko.parent().takeChild(realx)
- else:
- y = self.treeki.takeTopLevelItem(realx)
-
- del y
-
-
-
- def setupWidgets(self):
- frame = QtGui.QFrame()
- frameLayout = QtGui.QHBoxLayout(frame)
-
- # Displays the tiles
- self.tileDisplay = displayWidget()
-
- # Sets up the model for the tile pieces
- self.model = PiecesModel(self)
- self.tileDisplay.setModel(self.model)
-
- # Object List
- self.objectList = objectList()
- self.objmodel = QtGui.QStandardItemModel()
- SetupObjectModel(self.objmodel, Tileset.objects, Tileset.tiles)
- self.objectList.setModel(self.objmodel)
-
- self.tileWidget = tileOverlord()
-
- # Vertical Container A
- self.container = QtGui.QWidget()
- layout = QtGui.QVBoxLayout()
- layout.addWidget(self.tileDisplay)
- layout.addWidget(self.tileWidget)
- self.container.setLayout(layout)
-
-
- # Create the Group Tree
- self.treeki = QtGui.QTreeWidget()
- self.treeki.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.treeki.setDragEnabled(True)
- self.treeki.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
- self.treeki.setAcceptDrops(True)
- self.treeki.setDropIndicatorShown(True)
- self.treeki.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked)
- self.treeki.setColumnCount(2)
- self.treeki.setColumnWidth(0, 200)
-
- self.treeki.setColumnWidth(1, 40)
-
-
- # Vertical Container B
- self.treeBario = QtGui.QWidget()
- tlayout = QtGui.QVBoxLayout()
- tlayout.addWidget(self.treeki)
-
- self.groupbar = QtGui.QToolBar()
-
- self.newGroup = QtGui.QAction('New Group', self.groupbar)
- self.newGroup.triggered.connect(self.TriggerNewGroup)
- self.newGroup.setShortcut(QtGui.QKeySequence('Ctrl+Shift+N'))
-
- self.delGroup = QtGui.QAction('Delete Group', self.groupbar)
- self.delGroup.triggered.connect(self.TriggerDelGroup)
- self.delGroup.setShortcut(QtGui.QKeySequence('Ctrl+Shift+Del'))
-
- self.groupbar.addAction(self.newGroup)
- self.groupbar.addAction(self.delGroup)
-
- self.groupbar.setFloatable(False)
-
- tlayout.addWidget(self.groupbar)
- self.treeBario.setLayout(tlayout)
-
-
- # Creates the Tab Widget for behaviours and objects
- self.tabWidget = QtGui.QTabWidget()
-
- # Sets the Tabs
- self.tabWidget.addTab(self.objectList, 'Object List')
- self.tabWidget.addTab(self.treeBario, 'Object Groups')
-
-
-
- # Connections do things!
- self.objectList.clicked.connect(self.tileWidget.setObject)
- self.treeki.itemClicked.connect(connectToTileWidget)
-
-
- frameLayout.addWidget(self.container)
- frameLayout.addWidget(self.tabWidget)
-
-
- self.setCentralWidget(frame)
-
-
-
-
-#############################################################################################
-####################################### Main Function #######################################
-
-
-if __name__ == '__main__':
-
- import sys
-
- app = QtGui.QApplication(sys.argv)
- window = MainWindow()
- window.show()
- sys.exit(app.exec_())
- app.deleteLater() \ No newline at end of file
diff --git a/Puzzle+/windows_build.py b/Puzzle+/windows_build.py
deleted file mode 100755
index 10d710c..0000000
--- a/Puzzle+/windows_build.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from distutils.core import setup
-from py2exe.build_exe import py2exe
-import os, os.path, shutil, sys
-
-upxFlag = False
-if '-upx' in sys.argv:
- sys.argv.remove('-upx')
- upxFlag = True
-
-dir = 'distrib/windows'
-
-print '[[ Running Puzzle Through py2exe! ]]'
-print 'Note: Puzzle MUST have NSMBlib-0.5a or none at all to function.'
-print '>> Destination directory: %s' % dir
-sys.argv.append('py2exe')
-
-if os.path.isdir(dir): shutil.rmtree(dir)
-os.makedirs(dir)
-
-# exclude QtWebKit to save space, plus Python stuff we don't use
-excludes = ['encodings', 'doctest', 'pdb', 'unittest', 'difflib', 'inspect',
- 'os2emxpath', 'posixpath', 'optpath', 'locale', 'calendar',
- 'threading', 'select', 'socket', 'hashlib', 'multiprocessing', 'ssl',
- 'PyQt4.QtWebKit', 'PyQt4.QtNetwork']
-
-# set it up
-setup(
- name='Puzzle',
- version='1.0',
- description='Puzzle - Tileset Editor',
- windows=[
- {'script': 'puzzle.py',
- }
- ],
- options={'py2exe':{
- 'includes': ['sip', 'encodings', 'encodings.hex_codec', 'encodings.utf_8'],
- 'compressed': 1,
- 'optimize': 2,
- 'excludes': excludes,
- 'bundle_files': 3,
- 'dist_dir': dir
- }}
-)
-
-print '>> Built frozen executable!'
-
-# now that it's built, configure everything
-os.unlink(dir + '/w9xpopen.exe') # not needed
-
-if upxFlag:
- if os.path.isfile('upx.exe'):
- print '>> Found UPX, using it to compress the executables!'
- files = os.listdir(dir)
- upx = []
- for f in files:
- if f.endswith('.exe') or f.endswith('.dll') or f.endswith('.pyd'):
- upx.append('"%s/%s"' % (dir,f))
- os.system('upx -9 ' + ' '.join(upx))
- print '>> Compression complete.'
- else:
- print '>> UPX not found, binaries can\'t be compressed.'
- print '>> In order to build Reggie! with UPX, place the upx.exe file into '\
- 'this folder.'
-
-if os.path.isdir(dir + '/Icons'): shutil.rmtree(dir + '/Icons')
-if os.path.isdir(dir + '/nsmblib-0.5a'): shutil.rmtree(dir + '/nsmblib-0.5a')
-shutil.copytree('Icons', dir + '/Icons')
-shutil.copytree('nsmblib-0.5a', dir + '/nsmblib-0.5a')
-shutil.copy('license.txt', dir)
-
-print '>> Attempting to copy VC++2008 libraries...'
-if os.path.isdir('Microsoft.VC90.CRT'):
- shutil.copytree('Microsoft.VC90.CRT', dir + '/Microsoft.VC90.CRT')
- print '>> Copied libraries!'
-else:
- print '>> Libraries not found! The frozen executable will require the '\
- 'Visual C++ 2008 runtimes to be installed in order to work.'
- print '>> In order to automatically include the runtimes, place the '\
- 'Microsoft.VC90.CRT folder into this folder.'
-
-print '>> Reggie has been frozen to %s!' % dir