├── .gitattributes
├── .gitignore
├── CHANGES.txt
├── COPYING.txt
├── KDCEditor.pro
├── README.md
├── docs
├── images
│ ├── 7-1-1layer.png
│ ├── 7-1-2layers.png
│ ├── background.png
│ ├── diag-1layer.png
│ ├── diag-2layers.png
│ ├── edit.png
│ ├── mainwindow.png
│ ├── preview.png
│ └── properties.png
├── index.htm
└── style.css
├── kirbybowl-notes.txt
├── samples
├── KirbysRevCourseSampleSelects.kdc
├── carbowl.kdc
└── demo-6holes.kdc
└── src
├── TODO.txt
├── compress.c
├── compress.h
├── coursefiles.txt
├── coursewindow.cpp
├── coursewindow.h
├── coursewindow.ui
├── graphics.h
├── icons.qrc
├── images.qrc
├── images
├── 3dtiles-water.png
├── 3dtiles.png
├── bounce.png
├── bumpers.pdn
├── bumpers.png
├── conveyor.pdn
├── conveyor.png
├── dedede.pdn
├── dedede.png
├── enemies.pdn
├── enemies.png
├── gordo.pdn
├── gordo.png
├── gordo3d.png
├── grid.png
├── icons
│ ├── arrow_in.png
│ ├── arrow_redo.png
│ ├── arrow_undo.png
│ ├── brick_edit.png
│ ├── cut.png
│ ├── delete.png
│ ├── disk.png
│ ├── door_in.png
│ ├── folder.png
│ ├── help.png
│ ├── image.png
│ ├── magnifier.png
│ ├── page_copy.png
│ ├── paste_plain.png
│ ├── resultset_first.png
│ ├── resultset_last.png
│ ├── resultset_next.png
│ ├── resultset_previous.png
│ └── wand.png
├── kirby.png
├── main.icns
├── main.ico
├── main16.png
├── main32.png
├── main48.png
├── main64.png
├── mainicon.pdn
├── movers.pdn
├── movers.png
├── rotate.pdn
├── rotate.png
├── switches.png
├── terrain.pdn
├── terrain.png
├── tiletemplate.png
├── traps.pdn
├── traps.png
├── unknown.png
├── warps.pdn
├── warps.png
├── water.pdn
└── water.png
├── kirby.cpp
├── kirby.h
├── level.cpp
├── level.h
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── mapchange.cpp
├── mapchange.h
├── mapscene.cpp
├── mapscene.h
├── metatile.cpp
├── metatile.h
├── metatile_borders.cpp
├── metatile_obstacles.cpp
├── metatile_terrain.cpp
├── previewscene.cpp
├── previewscene.h
├── previewwindow.cpp
├── previewwindow.h
├── previewwindow.ui
├── propertieswindow.cpp
├── propertieswindow.h
├── propertieswindow.ui
├── romfile.cpp
├── romfile.h
├── tileeditwindow.cpp
├── tileeditwindow.h
├── tileeditwindow.ui
├── uthash.h
├── version.h
└── windows.rc
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | debug/
2 | release/
3 | Makefile
4 | Makefile.Release
5 | Makefile.Debug
6 | object_script.*
7 | *.pro.user*
8 | *.ini
9 | ui_*.h
10 | *.sfc
11 | currentlevel.txt
12 | *.autosave
13 |
14 | #################
15 | ## Eclipse
16 | #################
17 |
18 | *.pydevproject
19 | .project
20 | .metadata
21 | bin/
22 | tmp/
23 | *.tmp
24 | *.bak
25 | *.swp
26 | *~.nib
27 | local.properties
28 | .classpath
29 | .settings/
30 | .loadpath
31 |
32 | # External tool builders
33 | .externalToolBuilders/
34 |
35 | # Locally stored "Eclipse launch configurations"
36 | *.launch
37 |
38 | # CDT-specific
39 | .cproject
40 |
41 | # PDT-specific
42 | .buildpath
43 |
44 |
45 | #################
46 | ## Visual Studio
47 | #################
48 |
49 | ## Ignore Visual Studio temporary files, build results, and
50 | ## files generated by popular Visual Studio add-ons.
51 |
52 | # User-specific files
53 | *.suo
54 | *.user
55 | *.sln.docstates
56 |
57 | # Build results
58 |
59 | [Dd]ebug/
60 | [Rr]elease/
61 | x64/
62 | build/
63 | [Bb]in/
64 | [Oo]bj/
65 |
66 | # MSTest test Results
67 | [Tt]est[Rr]esult*/
68 | [Bb]uild[Ll]og.*
69 |
70 | *_i.c
71 | *_p.c
72 | *.ilk
73 | *.meta
74 | *.obj
75 | *.pch
76 | *.pdb
77 | *.pgc
78 | *.pgd
79 | *.rsp
80 | *.sbr
81 | *.tlb
82 | *.tli
83 | *.tlh
84 | *.tmp
85 | *.tmp_proj
86 | *.log
87 | *.vspscc
88 | *.vssscc
89 | .builds
90 | *.pidb
91 | *.log
92 | *.scc
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opensdf
99 | *.sdf
100 | *.cachefile
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 |
107 | # Guidance Automation Toolkit
108 | *.gpState
109 |
110 | # ReSharper is a .NET coding add-in
111 | _ReSharper*/
112 | *.[Rr]e[Ss]harper
113 |
114 | # TeamCity is a build add-in
115 | _TeamCity*
116 |
117 | # DotCover is a Code Coverage Tool
118 | *.dotCover
119 |
120 | # NCrunch
121 | *.ncrunch*
122 | .*crunch*.local.xml
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.Publish.xml
142 | *.pubxml
143 |
144 | # NuGet Packages Directory
145 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
146 | #packages/
147 |
148 | # Windows Azure Build Output
149 | csx
150 | *.build.csdef
151 |
152 | # Windows Store app package directory
153 | AppPackages/
154 |
155 | # Others
156 | sql/
157 | *.Cache
158 | ClientBin/
159 | [Ss]tyle[Cc]op.*
160 | ~$*
161 | *~
162 | *.dbmdl
163 | *.[Pp]ublish.xml
164 | *.pfx
165 | *.publishsettings
166 |
167 | # RIA/Silverlight projects
168 | Generated_Code/
169 |
170 | # Backup & report files from converting an old project file to a newer
171 | # Visual Studio version. Backup files are not needed, because we have git ;-)
172 | _UpgradeReport_Files/
173 | Backup*/
174 | UpgradeLog*.XML
175 | UpgradeLog*.htm
176 |
177 | # SQL Server files
178 | App_Data/*.mdf
179 | App_Data/*.ldf
180 |
181 | #############
182 | ## Windows detritus
183 | #############
184 |
185 | # Windows image file caches
186 | Thumbs.db
187 | ehthumbs.db
188 |
189 | # Folder config file
190 | Desktop.ini
191 |
192 | # Recycle Bin used on file shares
193 | $RECYCLE.BIN/
194 |
195 | # Mac crap
196 | .DS_Store
197 |
198 |
199 | #############
200 | ## Python
201 | #############
202 |
203 | *.py[co]
204 |
205 | # Packages
206 | *.egg
207 | *.egg-info
208 | dist/
209 | build/
210 | eggs/
211 | parts/
212 | var/
213 | sdist/
214 | develop-eggs/
215 | .installed.cfg
216 |
217 | # Installer logs
218 | pip-log.txt
219 |
220 | # Unit test / coverage reports
221 | .coverage
222 | .tox
223 |
224 | #Translations
225 | *.mo
226 |
227 | #Mr Developer
228 | .mr.developer.cfg
229 |
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | v1.13c [2015-06-10]
2 | Fix some occasional minor issues when loading/saving files
3 | (including memory access issues when saving on OS X)
4 |
5 | v1.13b [2015-03-29]
6 | Fixed occasional broken tiles for terrain 09 and 0C when placed against higher slopes
7 |
8 | v1.13 [2015-03-22]
9 | Much faster multithreaded saving
10 | Added loading/saving of individual level files
11 | Increased maximum tile height from 31 to 49 and max level width/length to 100
12 | (so you can waste more tile memory. Note that 2D and 3D map area limits are unchanged)
13 | Some more build/deployment improvements for OS X (thanks ConnorRK)
14 | Settings now saved in system-standard paths (%AppData%, etc.)
15 | Fixed buggy repainting of 2D map when scrolling horizontally
16 | Fixed possible bad graphics pointers when saving European ROMs
17 |
18 | v1.12 [2014-06-12]
19 | Fixed the rewritten 2D display exhibiting some visual strangeness when resizing levels
20 |
21 | v1.11 [2014-01-13]
22 | Fixed possible data corruption when saving levels to headered ROMs
23 | Heavily rewrote 2D map display (including better handling of font metrics)
24 | Tweaked compression code again (just slightly faster, probably the last change)
25 |
26 | v1.10b [2013-12-19]
27 | Fixed corrupted demo-6holes.kdc
28 |
29 | v1.10 [2013-12-18]
30 | Updated compression code based on exhal v1.20
31 | (now several times faster again)
32 | Added undo & redo commands/keys
33 | Fixed a critical bug that caused the Windows build to save corrupted .kdc files
34 | (due to gcc changing the behavior of sizeof() w/r/t bit fields for some reason)
35 | [huge thanks to DarkMatt for finding this]
36 |
37 | v1.00 [2013-09-15]
38 | Released source under MIT license
39 | Updated source and build to Qt 5
40 | Added sample course "demo-6holes.kdc" [by Revenant]
41 | Added sample course "KirbysRevCourseSampleSelects.kdc" [by DarkMatt]
42 | 2D view displays an indicator for layer 2 tiles
43 | Added support for bumpers on diagonal slope top/bottom tiles, and some 2-way slopes
44 | Fixed several Gordo path types sometimes behaving like invisible Gordos (?! HAL...)
45 | Fixed tiles for terrain 0x1A (slope northwest middle)
46 | Fixed tiles for obstacle 0x37 (east conveyor sloping down)
47 | Fixed east/west bumpers for north slope
48 | Fixed north/west bumpers of north/west slopes next to a flat tile with no bumpers
49 | Fixed tiles for adjacent rotating spaces
50 | Reworked some other tile data to be less broken sometimes
51 | Slopes on layer 1 next to terrain on layer 2 don't sometimes have messed up edges anymore
52 | Window maximized state and level image path are saved on exit
53 | More menu/toolbar actions are enabled/disabled when appropriate
54 | Current file name is displayed in titlebar
55 | Removed several unused icons
56 | Prevent main window being closed while file save is in progress
57 | Fixed small memory leaks
58 |
59 | v0.96 [2013-07-06]
60 | Fixed bug where a level being saved near the end of a ROM bank sometimes caused a bad pointer
61 | (i.e. sometimes a chunk of data would need to be moved to the beginning of the next bank
62 | when the current bank ran out of space, but the pointer to the data didn't reflect this
63 | because the pointer was written BEFORE moving to the next bank. oops...)
64 | [thanks DarkMatt for initially finding this]
65 |
66 | v0.95 [2013-06-27]
67 | Added sample course "carbowl.kdc" [thanks Kles]
68 | Replaced README.txt with HTML documentation
69 | Added course select dialog
70 | Added cut/copy/paste
71 | Added various keyboard shortcuts
72 | Added support for several unused rotating tile types
73 | Added "keep" layer option to preserve existing layer assignment
74 | Sprites are now displayed in preview window
75 | Applying obstacle numbers 24/30/31/32/33 now adjusts for slopes
76 | Fixed bug with selection area not being updated when changing levels or closing a ROM
77 | Fixed bug with scrollbar size remaining when closing ROM
78 | Fixed bug relating to layer 2 terrain drawing too many wall tiles when behind layer 1
79 | Fixed background 3 not displaying correctly in US/EU ROMs [thanks DarkMatt]
80 | Fixed missing tile for terrain 0x15
81 | Fixed messed up south bumper for terrain 0x07
82 | Setting terrain type 0x00 now fully removes other tile properties
83 | Removed some menu items for a few still-unimplemented actions
84 | Window positions/sizes and settings are saved on exit
85 |
86 | v0.90 [2013-06-15]:
87 | US/Europe ROM support
88 | Updated compression routine to make saving levels about 3-4x faster
89 | Editor now generates sprite clipping tables
90 | (this means Kirby won't appear to be on top of something when falling behind it anymore)
91 | Added load/save course files option
92 | Fixed "save level" menu item not doing anything
93 | Added notice to readme about putting Kracko and Whispy Woods in same level
94 | Editor now displays error when:
95 | - trying to save to a file that has been moved/deleted [thanks Kles]
96 | - saving a level whose 3D tile map becomes too large
97 | Fixed bugs relating to opening/closing ROMs with headers
98 | Handle possible corrupted ROMs a bit more gracefully
99 | Fixed bug where west slope had the wrong tiles for the east border [thanks Kles]
100 | Dedede's stage sprites/tiles now supported
101 | Changed some music titles to match the snesmusic.org set
102 | Bit of interface tweaks, mostly under the hood
103 | Updated README.txt (real documentation still forthcoming)
104 |
105 | v0.80b [2013-06-01]:
106 | First public release
107 | Fixed some border tiles for slope down to east
108 | Fixed maximum values for level length/width boxes
109 |
110 | v0.80 [2013-06-01]:
111 | Initial limited release
112 |
--------------------------------------------------------------------------------
/COPYING.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-2015 Devin Acker
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/KDCEditor.pro:
--------------------------------------------------------------------------------
1 | QT += core widgets
2 |
3 | QMAKE_CFLAGS += -std=c99
4 | QMAKE_CXXFLAGS += -std=c++11
5 |
6 | TARGET = KDCEditor
7 | TEMPLATE = app
8 | CONFIG += c++11
9 |
10 | CONFIG(debug, debug|release) {
11 | DESTDIR = debug
12 | }
13 | CONFIG(release, debug|release) {
14 | DESTDIR = release
15 | }
16 |
17 | OBJECTS_DIR = obj/$$DESTDIR
18 | MOC_DIR = $$OBJECTS_DIR
19 | RCC_DIR = $$OBJECTS_DIR
20 |
21 | # copy docs and samples on build
22 | copydata.commands += \
23 | $(COPY_DIR) \"$$PWD/docs\" $$DESTDIR &&\
24 | $(COPY_FILE) \"$$PWD/CHANGES.txt\" $$DESTDIR &&\
25 | $(COPY_FILE) \"$$PWD/COPYING.txt\" $$DESTDIR &&\
26 | $(COPY_DIR) \"$$PWD/samples\" $$DESTDIR
27 |
28 | first.depends = $(first) copydata
29 | export(first.depends)
30 | export(copydata.commands)
31 | QMAKE_EXTRA_TARGETS += first copydata
32 |
33 | # OS-specific metadata and stuff
34 | win32:RC_FILE = src/windows.rc
35 | macx:ICON = src/images/main.icns
36 |
37 | # build on OS X with xcode/clang and libc++
38 | macx:QMAKE_CXXFLAGS += -stdlib=libc++
39 |
40 | SOURCES += src/main.cpp\
41 | src/mainwindow.cpp \
42 | src/tileeditwindow.cpp \
43 | src/compress.c \
44 | src/level.cpp \
45 | src/kirby.cpp \
46 | src/mapscene.cpp \
47 | src/previewwindow.cpp \
48 | src/propertieswindow.cpp \
49 | src/romfile.cpp \
50 | src/metatile.cpp \
51 | src/metatile_terrain.cpp \
52 | src/metatile_borders.cpp \
53 | src/metatile_obstacles.cpp \
54 | src/coursewindow.cpp \
55 | src/previewscene.cpp \
56 | src/mapchange.cpp
57 |
58 | HEADERS += src/mainwindow.h \
59 | src/tileeditwindow.h \
60 | src/compress.h \
61 | src/level.h \
62 | src/kirby.h \
63 | src/mapscene.h \
64 | src/graphics.h \
65 | src/previewwindow.h \
66 | src/propertieswindow.h \
67 | src/romfile.h \
68 | src/metatile.h \
69 | src/coursewindow.h \
70 | src/version.h \
71 | src/previewscene.h \
72 | src/mapchange.h
73 |
74 | FORMS += src/mainwindow.ui \
75 | src/tileeditwindow.ui \
76 | src/previewwindow.ui \
77 | src/propertieswindow.ui \
78 | src/coursewindow.ui
79 |
80 | RESOURCES += \
81 | src/images.qrc \
82 | src/icons.qrc
83 |
84 | OTHER_FILES += \
85 | CHANGES.txt \
86 | README.md \
87 | src/TODO.txt \
88 | src/coursefiles.txt \
89 | src/windows.rc
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Kirby's Dream Course Editor
2 | ===========================
3 | version 1.13c
4 |
5 | 
6 |
7 | Here it is, warts and all. After many more months of hard work and laziness than there ever should have been, it's done. For now, anyway.
8 |
9 | This is a fully-featured level editor for the Super NES game Kirby's Dream Course (also known as Kirby Bowl). See the docs directory for a real introduction.
10 |
11 | More or less every issue that's come up since the initial beta release has been resolved, but there's still some stuff I may want to do eventually, which is all listed in TODO.txt.
12 |
13 | This is a Qt 5 project. So far it has been built and tested on Windows (with MinGW, because MSVC sucks), Mac OS X (with Clang), and Linux (with GCC). You are encouraged to try building and running it for yourself and seeing how it works. Other issues and suggestions are always welcome, of course.
14 |
15 | See the releases section for the latest official builds for Windows and OS X (both as of version 1.13).
16 |
17 | Some example course files are in the samples directory, courtesy of myself and others. Feel free to submit your own via pull request or email if you've made some cool levels that you want to show off.
18 |
--------------------------------------------------------------------------------
/docs/images/7-1-1layer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/7-1-1layer.png
--------------------------------------------------------------------------------
/docs/images/7-1-2layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/7-1-2layers.png
--------------------------------------------------------------------------------
/docs/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/background.png
--------------------------------------------------------------------------------
/docs/images/diag-1layer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/diag-1layer.png
--------------------------------------------------------------------------------
/docs/images/diag-2layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/diag-2layers.png
--------------------------------------------------------------------------------
/docs/images/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/edit.png
--------------------------------------------------------------------------------
/docs/images/mainwindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/mainwindow.png
--------------------------------------------------------------------------------
/docs/images/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/preview.png
--------------------------------------------------------------------------------
/docs/images/properties.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/docs/images/properties.png
--------------------------------------------------------------------------------
/docs/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Kirby's Dream Course Editor
7 |
8 |
9 |
10 |
11 |
12 | Kirby's Dream Course Editor
13 | version 1.13c
14 |
15 |
16 |
20 |
21 |
22 |
23 |
Introduction
24 | Kirby's Dream Course Editor (yes, still untitled) is a level editor for Kirby's Dream Course and its Japanese counterpart, Kirby Bowl .
25 |
26 | The editor supports full editing of all of the game's courses, including demo courses and unused test courses which are only present in the Japanese version. In addition, background and music selections can be changed in order to fully customize courses. Individual courses can be saved to standalone files for easy distribution, and sample levels are included to demonstrate some of the editor's capabilities.
27 |
28 | The editor supports US, European, and Japanese ROMs, both with or without copier headers. Special Tee Shot support is currently planned for future versions, but is not yet implemented due to numerous differences between the two games in how level and graphics data are organized.
29 |
30 | Source code is available on GitHub , and is distributed under the MIT license.
31 |
32 | (top )
33 |
34 |
35 |
The main window
36 |
37 |
38 | The main window is where all level editing takes place. When you load a ROM, level data is shown from a top-down perspective. Levels are composed of tiles, each one having a terrain type and an optional obstacle or enemy, as well as optional "bumpers" to the north, east, south, and west.
39 |
40 | Each tile displays its current height in the corner. Hovering over a tile with the mouse will also display its current terrain and obstacle values (in hexadecimal), which are also displayed in more detail on the window's status bar.
41 |
42 | Clicking and dragging will select a tile or range of tiles, which you can then edit by double-clicking, selecting the "Edit Tiles" option on the Edit menu or toolbar, or pressing the "E" key. This will bring up the edit window , allowing you to modify the properties of the selected tiles. To deselect tiles, right-click anywhere within the level view.
43 |
44 | To change levels, click the "course select" option on the Level menu or the toolbar. Use the "previous/next course/level" options to move back/forward one course or hole at a time, or hold Ctrl and use the left/right arrow keys (holding Shift at the same time will move by entire courses).
45 |
46 | (top )
47 |
48 |
49 |
The preview window
50 |
51 |
52 | The preview window appears alongside the main menu and shows what the current level will look like in game. This window displays the isometric tile maps which the editor generates and the game displays, so what you see here is exactly what you get in game.
53 |
54 | By default, the view in the preview window will automatically center on any tile that you move the mouse over in the main window, allowing you to view what you're editing without having to manually scroll the window. You can enable or disable this feature by selecting "Center Preview" in the Level menu or on the toolbar. Selecting "Show Preview" will redisplay the window if it is ever closed.
55 |
56 | Note that the preview window does not account for palette changes, due to using pre-made graphics rather than loading them from the ROM (mostly due to laziness). This may change in future versions.
57 |
58 | (top )
59 |
60 |
61 |
The edit window
62 |
63 |
64 | This is the window which appears when you double-click a selection of tiles or select the "Edit Tiles" menu option. From here, you can edit the terrain and obstacle values of a tile or tiles, increase/decrease tiles' height, add side bumpers, and select whether to display the tile's graphics on layer 1 or layer 2.
65 |
66 |
Terrain
67 | The Terrain dropdown allows you to select one of several terrain types for the currently selected tile or tiles. The available selections consist mostly of:
68 |
69 | Nothing: Selecting this option removes all terrain, obstacles, and other properties for the selected tile(s).
70 | Flat: The most common terrain type. Most obstacle/enemy types are intended for use on flat terrain.
71 | Slopes: There are four terrain types which represent terrain sloping downwards towards the four cardinal directions. These are the only types of slopes which can have bumpers on any side, except for the side at the bottom of the slope (i.e. a slope facing south can't have a south bumper, and so on).
72 |
73 | Note that most enemies can only be placed on slopes facing east or south. Placing enemies on other slopes will cause them to be invisible in game and can cause the game to lock up.
74 | Two-way slopes: These are "corner" slopes which face two adjacent cardinal directions. These consist of:
75 |
76 | "outer" slopes, which are used for things such as the corners of an island
77 | "inner" slopes, which are used for things such as the corners of a rectangular water hazard
78 |
79 |
80 | A good example of the difference between inner and outer slopes can be seen on 1P Course 2-1 in the original game; the "outer" slopes are used for the corners of the level, while the "inner" slopes are used in the sunken area in the center.
81 | Diagonal slopes: These slopes face northwest, northeast, southwest, and southeast. They are divided up into "top", "middle", and "bottom" parts; the top and bottom parts of the slope consist of both flat and sloped terrain. Bumpers can also be placed on the flat parts of diagonal slopes.
82 |
83 |
84 | If you are editing a range of tiles with multiple terrain types, the dropdown will default to a value of "(multiple)". This option causes each tile's existing terrain value to remain the same.
85 |
86 |
Height
87 | The height slider sets the height of the selected tile or tiles. Possible values range from 0 to 49 - this maximum value is not a limitation of the game, but is imposed by the editor to prevent isometric tilemaps from becoming too large (see the properties section for more details).
88 |
89 | If you are editing tiles which use one of the sloped terrain types, the height value must be at least 1. This is because all of the slope types start at the given height value and slope down to 1 unit lower.
90 |
91 | If you are editing a range of tiles with multiple height values, the slider will be labeled "Raise/Lower", and will allow you to raise or lower the selection by a relative amount.
92 |
93 |
Obstacles
94 | The obstacle dropdown allows you to put down the things that go on top of terrain, including both enemies and other static hazards such as sand traps, water, spikes, and pretty much everything else.
95 |
96 | A few things you should keep in mind when placing obstacles:
97 |
98 | Kirby's starting position is obstacle 0x0C, which should always appear once in each hole you design. Leaving this out won't cause the game to crash, but the player probably won't end up in a very useful location.
99 | Whispy Woods and Kracko cannot be used in the same level, or else their sprite and palette data will overwrite each other in memory, leading to glitched graphics.
100 | Water hazards and conveyor belts cannot be used together for the same reason.
101 | A level can only contain up to eight different enemy types (not including Whispy Woods and Kracko) due to similar memory restrictions.
102 | Enemies other than Whispy Woods and Kracko cannot be placed on slopes which do not face either east or south. Enemies placed on any other slope types will be invisible, and will cause the game to hang if it attempts to turn one of them into the hole.
103 | Water hazards, conveyor belts, and bounce pads all consist of multiple different obstacle numbers corresponding to direction and/or slope orientation. For your convenience, you only need to use the non-sloped types of each obstacle, and the editor will automatically adjust the obstacle numbers to account for slopes, if possible.
104 | Several types of rotating tile exist which are not used in any of the original levels. These include ones which are always on (regardless of switch state), ones which turn on when the switch is turned off (and vice-versa), and slow versions of all of the other types. Multiple types of rotating tile can be used together in one level, but avoid using both slow and normal-speed versions of the same rotating tiles, or graphical glitching will occur.
105 | All warp types are available numbered both 1 and 2, allowing you to have two different sets of possible warp exits. If you use any warps, there must be at least two with the same color and number, or else the game will hang when trying to determine Kirby's new location.
106 |
107 |
108 | If you are editing a range of tiles with multiple obstacle types, the dropdown will default to a value of "(multiple)", just like the terrain dropdown.
109 |
110 |
Bumpers
111 | These checkboxes allow you to place solid green bumpers on the north, south, east, and west edges of tiles. This is usually only useful for tiles which border empty space. Note that due to limited amounts of graphics assets, bumpers will only be visible when placed on flat tiles or slopes facing the four cardinal directions (including most "two-way" slopes).
112 |
113 | These checkboxes only cover bumpers which span the very edges of tiles. For horizontal, vertical, and diagonal bumpers which pass through the centers of tiles, use the obstacle dropdown.
114 |
115 | Also note that, due to the way the editor generates tile maps, there will be some situations in which the appearance of bumpers will not be 100% consistent with the way they appear in the original game. This is a purely cosmetic issue and is typically not very noticeable.
116 |
117 | If you are editing a range of tiles with multiple bumper on/off values in any direction, the respective checkbox will have a value of "partially checked". This value will cause all selected tiles to retain their existing settings for that individual bumper.
118 |
119 |
Layers
120 | During gameplay, graphics are divided up among three layers. The background is on layer 3, while the course is on layers 1 and 2. Most of the time, the ground is on layer 1, and non-sprite obstacles are on layer 2. However, there are times when terrain needs to overlap other terrain on screen, or times when Kirby can be behind some terrain while in front of other terrain. For these situations, you can cause selected tiles' terrain to appear on layer 2 instead, where it will be drawn on top of normal layer 1 terrain.
121 |
122 |
123 |
124 | 1P course 7-1 using only layer 1
125 |
126 | 1P course 7-1 using layers 1 and 2
127 |
128 |
129 | Note that Kirby is always drawn in front of layer 2, except when above empty space (i.e. if Kirby is immediately capable of going out of bounds), where Kirby can be between layers 1 and 2, or behind both layers.
130 |
131 | Another word of warning: when putting tiles on layer 2, any non-sprite obstacles will be drawn on layer 1, with the priority bit set so that they still appear on top of the terrain. Putting obstacles on layer 1 like this can cause visual issues with any layer 1 terrain which is close by on-screen, so use caution when putting obstacles on layer 2 tiles if they are also close to layer 1 tiles.
132 |
133 | In rare cases, you might place combinations of terrain which don't occur in the original levels, and thus don't have appropriate graphics available. If this happens, you can place one or more of the offending tiles on layer 2, causing them to be drawn separately instead of "connected" to adjacent terrain. One use for this is the ability to place diagonal slopes next to walls:
134 |
135 |
136 |
137 | Diagonal slopes on layer 1
138 |
139 | Diagonal slopes on layer 2
140 |
141 |
142 | If you are editing a range of tiles which use both layers, the "Keep" radio button will be selected by default. As with the other editing controls, this option causes the selected tiles' layer settings to be left alone.
143 |
144 | (top )
145 |
146 |
147 |
The properties window
148 |
149 |
150 | The properties window allows you to customize some details of both the current course and the specific hole you are editing.
151 |
Course properties
152 | The course properties controls allow you to change the current course's background and palette settings.
153 |
154 | Note that, as the text in the screenshot says, changing a course's background always effects multiple courses. For example, if you change 1P Course 1's background, the same change will be applied to 1P Extra 1, 2P Course 1, and 2P Extra 1.
155 |
156 | Palettes, unlike backgrounds, only affect the current course. The editor allows you to choose palettes for both the ground and the water hazards. Normally, water hazards use the same-numbered palette as the ground, but different combinations are also possible.
157 |
Level properties
158 | The level properties controls allow you to change a level's dimensions and background music.
159 |
160 | Kirby's Dream Course normally uses the same music tracks for several holes in a row, but that kind of consistency isn't actually a requirement - you can just as easily use 8 different music tracks in one course if you want to.
161 |
162 | Note that, because the Japanese version's intro sequence was removed from the US/European versions, its music track is only usable in the Japanese version.
163 |
164 | The level length and width boxes allow you to resize the level (length = north to south, width = east to west). Due to memory restrictions, the two-dimensional area of a level cannot be greater than 2,048; all of the original levels are much smaller in area than this, so this limitation is not likely to be much of a problem.
165 |
166 | Note that the isometric tilemaps are also limited in size; their size is determined by the length, width, and height of the level, as well as how much (or how little) of the level is empty space. Once again, the limit here is much higher than what any of the original levels reach, so this should only present a problem if you are making unusually large levels that come close to the length/width limits. In the event that this actually happens, you will be presented with a warning when saving your ROM, and part of your level will not be visible in game.
167 |
168 | (top )
169 |
170 |
171 |
Credits
172 | The author would like to extend well-earned salami sandwiches to each of the following individuals and groups:
173 |
174 |
175 | borokobo / PureSabe for creating KBE and KBEAssist , the Kirby Bowl hacking tools (in Japanese) which partially inspired the creation of this one
176 | creaothceann for vSNES, which I used extensively to painstakingly put together tile map data from the original levels
177 | The Cutting Room Floor and #tcrf for moral support, listening to me blather on about this thing all the time, and getting me into this mess (and giving me a reason to dig into over a hundred games and counting)
178 | DarkMatt for testing and some example levels
179 | Disnesquick for SPASMDK
180 | Geiger for his snes9x debugger
181 | Graham for his 65xx series reference
182 | Kles for extensive testing, suggestions, and some cool example levels
183 | Mark James (famfamfam ) for the Silk Icons set, used for menu and toolbar graphics
184 | Raccoon Sam and ConnorRK for build assistance and testing on OS X
185 | /r/KDCGameGrumps for much more testing, feedback, and general enjoyment
186 | Everyone else who has downloaded and supported the development of this program in any way
187 |
188 |
189 | (top )
190 |
191 |
192 |
What's new
193 | See the full changelog for a complete version history.
194 |
195 | (top )
196 |
197 |
198 |
Remarks
199 | As of writing the documentation for version 1.00, it's been a little over half nearly a year since I started researching Kirby's Dream Course , and it wasn't too much longer before I started working on this editor. I was itching for a cool ROM hacking project to undertake, and I had coincidentally been digging into this game to research the Japanese version's debug mode for an article on The Cutting Room Floor.
200 |
201 | At some point while messing around with Kirby Bowl , I stumbled upon the game's decompression routine, rewrote it in the form of a small tool called "exhal", and began exploring the game's data. Most of it quickly became clear, and I set out to write a document on the level format used by the game.
202 |
203 | Around that time, I semi-publically announced my intention to write an editor, and by chance I found video footage on Nico Nico Douga of a Japanese hacker's custom Kirby Bowl levels, and was later given a link to their editor, "KBE". While the original editor was a bit lacking in the interface department, this obscure tool and the hacked Kirby Bowl videos proved that there was an audience for this sort of thing, and I resolved to make a more accessible alternative.
204 |
205 | A couple of months later, delayed by my acquiring a full-time job, I had a rudimentary level editor put together. It still needed to generate the pre-made isometric tile maps that the game actually displayed, so I set out opening up dozens of save states and punching in what ended up as just a little bit under 3,500 lines of raw tile data, eight tiles per line. The lack of a clear-cut pattern in the way the game's graphics were organized, along with some annoying properties of the visual perspective, made this a monstrously tedious affair.
206 |
207 | Nevertheless, it was eventually done, and after patching up some quirks in the tile map generation code, the editor was finally almost ready for prime time. About two months of testing and user feedback later (and several more months of laziness), and version 1.00 is finally here.
208 |
209 | At this point, I'm probably going to take a break to work on something else (or nothing at all), but comments, etc., will always be welcome.
210 |
211 | Special Tee Shot support is still in the cards, but it'll be a lot more tedious tile-wrangling before that happens "for real".
212 |
213 | Oh, and as the old text readme mentioned, this is my first real GUI application ever. I'm sorry that it doesn't look very good. (I'm also sorry that the download for the Windows build is so much larger than before, as I updated to Qt 5 but didn't ever have time to build smaller Qt DLLs with fewer dependencies. At least you can delete the Qt 4 DLLs from previous versions.)
214 |
215 | (top )
216 |
217 |
218 |
Contact
219 | If you want to yell at me about something, here's how you can:
220 |
221 | Email :
222 | d@revenant1.net
223 |
224 | IRC :
225 | "devin" on irc.badnik.net
226 | "Revenant" on irc.oftc.net
227 | "Revenant`" on irc.synirc.net and irc.dal.net
228 |
229 | Forums :
230 | http://jul.rustedlogic.net/profile.php?id=504
231 | http://www.romhacking.net/forum/index.php?action=profile;u=10455
232 |
233 | (top )
234 |
235 |
236 | copyright 2013-2015 Devin "Revenant" Acker - "life is but a dream"
237 |
238 |
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | font-family: verdana, sans-serif;
3 | color: black;
4 | clear: both;
5 | }
6 |
7 | h2 {
8 | font-family: verdana, sans-serif;
9 | color: black;
10 | }
11 |
12 | body {
13 | font-family: sans-serif;
14 | color: black;
15 | background: url("images/background.png");
16 | }
17 |
18 | a:link {
19 | color: blue;
20 | text-decoration: none;
21 | }
22 |
23 | a:active {
24 | color: blue;
25 | }
26 |
27 | a:hover {
28 | color: blue;
29 | text-decoration: underline;
30 | }
31 |
32 | a:visited {
33 | color: blue;
34 | text-decoration: none;
35 | }
36 |
37 | span.smallLink {
38 | font-size: 75%;
39 | }
40 |
41 | img.right {
42 | float: right;
43 | padding: 4px;
44 | }
45 |
46 | img.center {
47 | display: block;
48 | margin-left: auto;
49 | margin-right: auto;
50 | }
51 |
52 | table {
53 | background-color: #FFE0F0;
54 | border-collapse: collapse;
55 | margin-left: auto;
56 | margin-right: auto;
57 | }
58 |
59 | td {
60 | border: solid 2px;
61 | border-color: #FFC0C8;
62 | padding: 3px;
63 | text-align: center;
64 | }
--------------------------------------------------------------------------------
/samples/KirbysRevCourseSampleSelects.kdc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/samples/KirbysRevCourseSampleSelects.kdc
--------------------------------------------------------------------------------
/samples/carbowl.kdc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/samples/carbowl.kdc
--------------------------------------------------------------------------------
/samples/demo-6holes.kdc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/samples/demo-6holes.kdc
--------------------------------------------------------------------------------
/src/TODO.txt:
--------------------------------------------------------------------------------
1 | Interface/misc:
2 | - use tr() more when appropriate (for future translation support)
3 | - kirby-related toolbar/menu/mainwindow icons
4 | - move terrain/obstacle enums into kirby.h or something
5 | - emulator preview button/asm hack
6 | - use QHash/QMap for metatile data
7 | - much smaller build of Qt5 binaries on windows
8 |
9 | 2D view:
10 | - less bad programmer art
11 |
12 | 3D view:
13 | - rest of possible bumpers for 2way slopes (sometimes with gaps in corners if necessary)
14 | - come up with a way to fix the QPixmap -> QImage -> QPixmap bottleneck when flipping 8x8 tiles
15 |
--------------------------------------------------------------------------------
/src/compress.c:
--------------------------------------------------------------------------------
1 | /*
2 | exhal / inhal (de)compression routines
3 |
4 | This code is released under the terms of the MIT license.
5 | See COPYING.txt for details.
6 |
7 | Copyright (c) 2013-2015 Devin Acker
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in
17 | all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | THE SOFTWARE.
26 |
27 | */
28 |
29 | #include
30 | #include
31 | #include "compress.h"
32 | #include "uthash.h"
33 |
34 | #ifdef DEBUG_OUT
35 | #define debug(...) printf(__VA_ARGS__)
36 | #else
37 | #define debug(...)
38 | #endif
39 |
40 | // compression method values for backref_t and rle_t
41 | typedef enum {
42 | rle_8 = 0,
43 | rle_16 = 1,
44 | rle_seq = 2,
45 |
46 | lz_norm = 0,
47 | lz_rot = 1,
48 | lz_rev = 2
49 | } method_e;
50 |
51 | // used to store and compare backref candidates
52 | typedef struct {
53 | uint16_t offset, size;
54 | method_e method;
55 | } backref_t;
56 |
57 | // used to store RLE candidates
58 | typedef struct {
59 | uint16_t size, data;
60 | method_e method;
61 | } rle_t;
62 |
63 | // used to hash and index byte tuples
64 | typedef struct {
65 | int bytes;
66 | uint16_t offset;
67 | UT_hash_handle hh;
68 | } tuple_t;
69 | // turn 4 bytes into a single integer for quicker hashing/searching
70 | #define COMBINE(w, x, y, z) ((w << 24) | (x << 16) | (y << 8) | z)
71 |
72 | uint8_t rotate (uint8_t);
73 | rle_t rle_check (uint8_t*, uint8_t*, uint32_t, int);
74 | backref_t ref_search (uint8_t*, uint8_t*, uint32_t, tuple_t*, int);
75 | uint16_t write_backref (uint8_t*, uint16_t, backref_t);
76 | uint16_t write_rle (uint8_t*, uint16_t, rle_t);
77 | uint16_t write_raw (uint8_t*, uint16_t, uint8_t*, uint16_t);
78 | void free_offsets(tuple_t*);
79 |
80 | // Compresses a file of up to 64 kb.
81 | // unpacked/packed are 65536 byte buffers to read/from write to,
82 | // inputsize is the length of the uncompressed data.
83 | // Returns the size of the compressed data in bytes, or 0 if compression failed.
84 | size_t pack(uint8_t *unpacked, size_t inputsize, uint8_t *packed, int fast) {
85 | if (inputsize > DATA_SIZE) return 0;
86 |
87 | // current input/output positions
88 | uint32_t inpos = 0;
89 | uint32_t outpos = 0;
90 |
91 | // backref and RLE compression candidates
92 | backref_t backref;
93 | rle_t rle;
94 |
95 | // used to collect data which should be written uncompressed
96 | uint8_t dontpack[LONG_RUN_SIZE];
97 | uint16_t dontpacksize = 0;
98 |
99 | // index of first locations of byte-tuples used to speed up LZ string search
100 | tuple_t *offsets = NULL;
101 |
102 | debug("inputsize = %d\n", inputsize);
103 |
104 | for (uint16_t i = 0; inputsize >= 4 && i < inputsize - 4; i++) {
105 | tuple_t *tuple;
106 | int currbytes = COMBINE(unpacked[i], unpacked[i+1], unpacked[i+2], unpacked[i+3]);
107 |
108 | // has this one been indexed already
109 | HASH_FIND_INT(offsets, &currbytes, tuple);
110 | if (!tuple) {
111 | tuple = (tuple_t*)malloc(sizeof(tuple_t));
112 | tuple->bytes = currbytes;
113 | tuple->offset = i;
114 | HASH_ADD_INT(offsets, bytes, tuple);
115 | }
116 | }
117 |
118 | while (inpos < inputsize) {
119 | // check for a potential RLE
120 | rle = rle_check(unpacked, unpacked + inpos, inputsize, fast);
121 | // check for a potential back reference
122 | if (rle.size < LONG_RUN_SIZE && inputsize >= 3 && inpos < inputsize - 3)
123 | backref = ref_search(unpacked, unpacked + inpos, inputsize, offsets, fast);
124 | else backref.size = 0;
125 |
126 | // if the backref is a better candidate, use it
127 | if (backref.size > 3 && backref.size > rle.size) {
128 | if (outpos + dontpacksize + backref.size >= DATA_SIZE) {
129 | free_offsets(offsets);
130 | return 0;
131 | }
132 |
133 | // flush the raw data buffer first
134 | outpos += write_raw(packed, outpos, dontpack, dontpacksize);
135 | dontpacksize = 0;
136 |
137 | outpos += write_backref(packed, outpos, backref);
138 | inpos += backref.size;
139 | }
140 | // or if the RLE is a better candidate, use it instead
141 | else if (rle.size >= 2) {
142 | if (outpos + dontpacksize + rle.size >= DATA_SIZE) {
143 | free_offsets(offsets);
144 | return 0;
145 | }
146 |
147 | // flush the raw data buffer first
148 | outpos += write_raw(packed, outpos, dontpack, dontpacksize);
149 | dontpacksize = 0;
150 |
151 | outpos += write_rle(packed, outpos, rle);
152 | inpos += rle.size;
153 |
154 | }
155 | // otherwise, write this byte uncompressed
156 | else {
157 | dontpack[dontpacksize++] = unpacked[inpos++];
158 |
159 | if (outpos + dontpacksize >= DATA_SIZE) {
160 | free_offsets(offsets);
161 | return 0;
162 | }
163 |
164 | // if the raw data buffer is full, flush it
165 | if (dontpacksize == LONG_RUN_SIZE) {
166 | outpos += write_raw(packed, outpos, dontpack, dontpacksize);
167 | dontpacksize = 0;
168 | }
169 | }
170 | }
171 |
172 | // flush any remaining uncompressed data
173 | if (outpos + dontpacksize + 1 > DATA_SIZE) {
174 | free_offsets(offsets);
175 | return 0;
176 | }
177 |
178 | outpos += write_raw(packed, outpos, dontpack, dontpacksize);
179 |
180 | //add the terminating byte
181 | packed[outpos++] = 0xFF;
182 |
183 | free_offsets(offsets);
184 | return (size_t)outpos;
185 | }
186 |
187 | void free_offsets(tuple_t *offsets) {
188 | tuple_t *curr, *temp;
189 | HASH_ITER(hh, offsets, curr, temp) {
190 | HASH_DEL(offsets, curr);
191 | free(curr);
192 | }
193 | }
194 |
195 | // Decompresses a file of up to 64 kb.
196 | // unpacked/packed are 65536 byte buffers to read/from write to,
197 | // Returns the size of the uncompressed data in bytes or 0 if decompression failed.
198 | size_t unpack(uint8_t *packed, uint8_t *unpacked) {
199 | // current input/output positions
200 | uint32_t inpos = 0;
201 | uint32_t outpos = 0;
202 |
203 | uint8_t input;
204 | uint16_t command, length, offset;
205 | int methoduse[7] = {0};
206 |
207 | while (1) {
208 | // read command byte from input
209 | input = packed[inpos++];
210 |
211 | // command 0xff = end of data
212 | if (input == 0xFF)
213 | break;
214 |
215 | // check if it is a long or regular command, get the command no. and size
216 | if ((input & 0xE0) == 0xE0) {
217 | command = (input >> 2) & 0x07;
218 | // get LSB of length from next byte
219 | length = (((input & 0x03) << 8) | packed[inpos++]) + 1;
220 | } else {
221 | command = input >> 5;
222 | length = (input & 0x1F) + 1;
223 | }
224 |
225 | // don't try to decompress > 64kb
226 | if (((command == 2) && (outpos + 2*length > DATA_SIZE))
227 | || (outpos + length > DATA_SIZE)) {
228 | return 0;
229 | }
230 |
231 | switch (command) {
232 | // write uncompressed bytes
233 | // (note: i had to go back to using loops for all of these because memcpy/memmove
234 | // were both fucking up and handling these inconsistently between versions of gcc)
235 | case 0:
236 | memcpy(&unpacked[outpos], &packed[inpos], length);
237 |
238 | outpos += length;
239 | inpos += length;
240 | break;
241 |
242 | // 8-bit RLE
243 | case 1:
244 | for (int i = 0; i < length; i++)
245 | unpacked[outpos++] = packed[inpos];
246 |
247 | inpos++;
248 | break;
249 |
250 | // 16-bit RLE
251 | case 2:
252 | for (int i = 0; i < length; i++) {
253 | unpacked[outpos++] = packed[inpos];
254 | unpacked[outpos++] = packed[inpos+1];
255 | }
256 |
257 | inpos += 2;
258 | break;
259 |
260 | // 8-bit increasing sequence
261 | case 3:
262 | for (int i = 0; i < length; i++)
263 | unpacked[outpos++] = packed[inpos] + i;
264 |
265 | inpos++;
266 | break;
267 |
268 | // regular backref
269 | // (offset is big-endian)
270 | case 4:
271 | case 7:
272 | // 7 isn't a real method number, but it behaves the same as 4 due to a quirk in how
273 | // the original decompression routine is programmed. (one of Parasyte's docs confirms
274 | // this for GB games as well). let's handle it anyway
275 | command = 4;
276 |
277 | offset = (packed[inpos] << 8) | packed[inpos+1];
278 | for (int i = 0; i < length; i++)
279 | unpacked[outpos++] = unpacked[offset + i];
280 |
281 | inpos += 2;
282 | break;
283 |
284 | // backref with bit rotation
285 | // (offset is big-endian)
286 | case 5:
287 | offset = (packed[inpos] << 8) | packed[inpos+1];
288 | for (int i = 0; i < length; i++)
289 | unpacked[outpos++] = rotate(unpacked[offset + i]);
290 |
291 | inpos += 2;
292 | break;
293 |
294 | // backwards backref
295 | // (offset is big-endian)
296 | case 6:
297 | offset = (packed[inpos] << 8) | packed[inpos+1];
298 | for (int i = 0; i < length; i++)
299 | unpacked[outpos++] = unpacked[offset - i];
300 |
301 | inpos += 2;
302 | }
303 |
304 | // keep track of how many times each compression method is used
305 | methoduse[command]++;
306 | }
307 |
308 | #ifdef EXTRA_OUT
309 | printf("Method Uses\n");
310 | printf("No compression : %i\n", methoduse[0]);
311 | printf("RLE (8-bit) : %i\n", methoduse[1]);
312 | printf("RLE (16-bit) : %i\n", methoduse[2]);
313 | printf("RLE (sequence) : %i\n", methoduse[3]);
314 | printf("Backref (normal) : %i\n", methoduse[4]);
315 | printf("Backref (rotate) : %i\n", methoduse[5]);
316 | printf("Backref (reverse): %i\n", methoduse[6]);
317 |
318 | printf("\nCompressed size: %u bytes\n", inpos);
319 | #endif
320 |
321 | return (size_t)outpos;
322 | }
323 |
324 | // Decompress data from an offset into a file
325 | size_t unpack_from_file (FILE *file, size_t offset, uint8_t *unpacked) {
326 | uint8_t packed[DATA_SIZE];
327 |
328 | fseek(file, offset, SEEK_SET);
329 | fread((void*)packed, DATA_SIZE, 1, file);
330 | if (!ferror(file))
331 | return unpack(packed, unpacked);
332 |
333 | return 0;
334 | }
335 |
336 | // Reverses the order of bits in a byte.
337 | // One of the back reference methods does this. As far as game data goes, it seems to be
338 | // pretty useful for compressing graphics.
339 | uint8_t rotate (uint8_t i) {
340 | uint8_t j = 0;
341 | if (i & 0x01) j |= 0x80;
342 | if (i & 0x02) j |= 0x40;
343 | if (i & 0x04) j |= 0x20;
344 | if (i & 0x08) j |= 0x10;
345 | if (i & 0x10) j |= 0x08;
346 | if (i & 0x20) j |= 0x04;
347 | if (i & 0x40) j |= 0x02;
348 | if (i & 0x80) j |= 0x01;
349 |
350 | return j;
351 | }
352 |
353 | // Searches for possible RLE compressed data.
354 | // start and current are positions within the uncompressed input stream.
355 | // fast enables faster compression by ignoring sequence RLE.
356 | rle_t rle_check (uint8_t *start, uint8_t *current, uint32_t insize, int fast) {
357 | rle_t candidate = { 0, 0, 0 };
358 | size_t size;
359 |
360 | // check for possible 8-bit RLE
361 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
362 | if (current[size] != current[0]) break;
363 |
364 | // if this is better than the current candidate, use it
365 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
366 | if (size > 2 && size > candidate.size) {
367 | candidate.size = size;
368 | candidate.data = current[0];
369 | candidate.method = rle_8;
370 |
371 | debug("\trle_check: found new candidate (size = %d, method = %d)\n", candidate.size, candidate.method);
372 | }
373 |
374 | // check for possible 16-bit RLE
375 | uint16_t first = current[0] | (current[1] << 8);
376 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize - 1; size += 2) {
377 | uint16_t next = current[size] | (current[size + 1] << 8);
378 | if (next != first) break;
379 | }
380 |
381 | // if this is better than the current candidate, use it
382 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
383 | if (size > 2 && size > candidate.size) {
384 | candidate.size = size;
385 | candidate.data = first;
386 | candidate.method = rle_16;
387 |
388 | debug("\trle_check: found new candidate (size = %d, method = %d)\n", candidate.size, candidate.method);
389 | }
390 |
391 | // fast mode: don't use sequence RLE
392 | if (fast) return candidate;
393 |
394 | // check for possible sequence RLE
395 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
396 | if (current[size] != (current[0] + size)) break;
397 |
398 | // if this is better than the current candidate, use it
399 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
400 | if (size > 2 && size > candidate.size) {
401 | candidate.size = size;
402 | candidate.data = current[0];
403 | candidate.method = rle_seq;
404 |
405 | debug("\trle_check: found new candidate (size = %d, method = %d)\n", candidate.size, candidate.method);
406 | }
407 |
408 | return candidate;
409 | }
410 |
411 | // Searches for the best possible back reference.
412 | // start and current are positions within the uncompressed input stream.
413 | // fast enables fast mode which only uses regular forward references
414 | backref_t ref_search (uint8_t *start, uint8_t *current, uint32_t insize, tuple_t *offsets, int fast) {
415 | backref_t candidate = { 0, 0, 0 };
416 | uint16_t size;
417 | int currbytes;
418 | tuple_t *tuple;
419 |
420 | // references to previous data which goes in the same direction
421 | // see if this byte pair exists elsewhere, then start searching.
422 | currbytes = COMBINE(current[0], current[1], current[2], current[3]);
423 | HASH_FIND_INT(offsets, &currbytes, tuple);
424 | if (tuple) for (uint8_t *pos = start + tuple->offset; pos < current; pos++) {
425 | // see how many bytes in a row are the same between the current uncompressed data
426 | // and the data at the position being searched
427 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
428 | if (pos[size] != current[size]) break;
429 |
430 | // if this is better than the current candidate, use it
431 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
432 | if (size > 3 && size > candidate.size) {
433 | candidate.size = size;
434 | candidate.offset = pos - start;
435 | candidate.method = lz_norm;
436 |
437 | debug("\tref_search: found new candidate (offset: %4x, size: %d, method = %d)\n", candidate.offset, candidate.size, candidate.method);
438 | }
439 | }
440 |
441 | // fast mode: forward references only
442 | if (fast) return candidate;
443 |
444 | // references to data where the bits are rotated
445 | // see if this byte pair exists elsewhere, then start searching.
446 | currbytes = COMBINE(rotate(current[0]), rotate(current[1]), rotate(current[2]), rotate(current[3]));
447 | HASH_FIND_INT(offsets, &currbytes, tuple);
448 | if (tuple) for (uint8_t *pos = start + tuple->offset; pos < current; pos++) {
449 | // now repeat the check with the bit rotation method
450 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
451 | if (pos[size] != rotate(current[size])) break;
452 |
453 | // if this is better than the current candidate, use it
454 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
455 | if (size > 3 && size > candidate.size) {
456 | candidate.size = size;
457 | candidate.offset = pos - start;
458 | candidate.method = lz_rot;
459 |
460 | debug("\tref_search: found new candidate (offset: %4x, size: %d, method = %d)\n", candidate.offset, candidate.size, candidate.method);
461 | }
462 | }
463 |
464 | // references to data which goes backwards
465 | // see if this byte pair exists elsewhere, then start searching.
466 | currbytes = COMBINE(current[3], current[2], current[1], current[0]);
467 | HASH_FIND_INT(offsets, &currbytes, tuple);
468 | // add 3 to offset since we're starting at the end of the 4 byte sequence here
469 | if (tuple) for (uint8_t *pos = start + tuple->offset + 3; pos < current; pos++) {
470 | // now repeat the check but go backwards
471 | for (size = 0; size <= LONG_RUN_SIZE && current + size < start + insize; size++)
472 | if (start[pos - start - size] != current[size]) break;
473 |
474 | // if this is better than the current candidate, use it
475 | if (size > LONG_RUN_SIZE) size = LONG_RUN_SIZE;
476 | if (size > 3 && size > candidate.size) {
477 | candidate.size = size;
478 | candidate.offset = pos - start;
479 | candidate.method = lz_rev;
480 |
481 | debug("\tref_search: found new candidate (offset: %4x, size: %d, method = %d)\n", candidate.offset, candidate.size, candidate.method);
482 | }
483 | }
484 |
485 | return candidate;
486 | }
487 |
488 | // Writes a back reference to the compressed output stream.
489 | // Returns number of bytes written
490 | uint16_t write_backref (uint8_t *out, uint16_t outpos, backref_t backref) {
491 | uint16_t size = backref.size - 1;
492 | int outsize;
493 |
494 | debug("write_backref: writing backref to %4x, size %d (method %d)\n", backref.offset, backref.size, backref.method);
495 |
496 | // long run
497 | if (size >= RUN_SIZE) {
498 | // write command byte / MSB of size
499 | out[outpos++] = (0xF0 + (backref.method << 2)) | (size >> 8);
500 | // write LSB of size
501 | out[outpos++] = size & 0xFF;
502 |
503 | outsize = 4;
504 | }
505 | // normal size run
506 | else {
507 | // write command byte / size
508 | out[outpos++] = (0x80 + (backref.method << 5)) | size;
509 | outsize = 3;
510 | }
511 |
512 | // write MSB of offset
513 | out[outpos++] = backref.offset >> 8;
514 | // write LSB of offset
515 | out[outpos++] = backref.offset & 0xFF;
516 |
517 | return outsize;
518 | }
519 |
520 | // Writes RLE data to the compressed output stream.
521 | // Returns number of bytes written
522 | uint16_t write_rle (uint8_t *out, uint16_t outpos, rle_t rle) {
523 | uint16_t size;
524 | int outsize;
525 |
526 | if (rle.method == rle_16)
527 | size = (rle.size / 2) - 1;
528 | else
529 | size = rle.size - 1;
530 |
531 | debug("write_rle: writing %d bytes of data 0x%02x (method %d)\n", rle.size, rle.data, rle.method);
532 |
533 | // long run
534 | if (size >= RUN_SIZE) {
535 | // write command byte / MSB of size
536 | out[outpos++] = (0xE4 + (rle.method << 2)) | (size >> 8);
537 | // write LSB of size
538 | out[outpos++] = size & 0xFF;
539 |
540 | outsize = 3;
541 | }
542 | // normal size run
543 | else {
544 | // write command byte / size
545 | out[outpos++] = (0x20 + (rle.method << 5)) | size;
546 |
547 | outsize = 2;
548 | }
549 |
550 | out[outpos++] = rle.data;
551 | // write upper byte of 16-bit RLE (and adjust written data size)
552 | if (rle.method == rle_16) {
553 | out[outpos++] = rle.data >> 8;
554 | outsize++;
555 | }
556 |
557 | return outsize;
558 | }
559 |
560 | // Write uncompressed data to the output stream.
561 | // Returns number of bytes written.
562 | uint16_t write_raw (uint8_t *out, uint16_t outpos, uint8_t *in, uint16_t insize) {
563 | if (!insize) return 0;
564 |
565 | #ifdef DEBUG_OUT
566 | printf("write_raw: writing %d bytes unpacked data: ", insize);
567 | for (int i = 0; i < insize; i++)
568 | printf("%02x ", in[i]);
569 |
570 | printf("\n");
571 | #endif
572 |
573 | uint16_t size = insize - 1;
574 | int outsize;
575 |
576 | if (size >= RUN_SIZE) {
577 | // write command byte + MSB of size
578 | out[outpos++] = 0xE0 + (size >> 8);
579 | // write LSB of size
580 | out[outpos++] = size & 0xFF;
581 |
582 | outsize = insize + 2;
583 | }
584 | // normal size run
585 | else {
586 | // write command byte / size
587 | out[outpos++] = size;
588 |
589 | outsize = insize + 1;
590 | }
591 |
592 | // write data
593 | memcpy(&out[outpos], in, insize);
594 |
595 | return outsize;
596 | }
597 |
--------------------------------------------------------------------------------
/src/compress.h:
--------------------------------------------------------------------------------
1 | /*
2 | exhal / inhal (de)compression routines
3 |
4 | Copyright (c) 2013 Devin Acker
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
24 | */
25 |
26 | #ifndef _COMPRESS_H
27 | #define _COMPRESS_H
28 |
29 | #ifdef __cplusplus
30 | extern "C" {
31 | #endif
32 |
33 | #include
34 | #include
35 | #include
36 |
37 | #define DATA_SIZE 65536
38 | #define RUN_SIZE 32
39 | #define LONG_RUN_SIZE 1024
40 |
41 | size_t pack (uint8_t *unpacked, size_t inputsize, uint8_t *packed, int fast);
42 | size_t unpack (uint8_t *packed, uint8_t *unpacked);
43 |
44 | size_t unpack_from_file (FILE *file, size_t offset, uint8_t *unpacked);
45 |
46 | #ifdef __cplusplus
47 | }
48 | #endif
49 |
50 | // end include guard
51 | #endif
52 |
--------------------------------------------------------------------------------
/src/coursefiles.txt:
--------------------------------------------------------------------------------
1 | KDC course file format:
2 |
3 | Offset Size Description
4 | -------------------------------------------------
5 | 0 4 String "KDC", null-terminated
6 | 4 1 Game (0 = KDC, 1 = STS)
7 | 5 1 Background number
8 | 6 1 Terrain palette
9 | 7 1 Water palette
10 | 8 8 Music list (one byte per level)
11 | 16 4*8 Pointers to individual levels in file
12 | (a null pointer means level is not present in file; don't try to load it)
13 |
14 | Each individual level consists of the header, followed by the tile data as maptile_t:
15 | (west to east, north to south, w * l * 4 bytes)
16 |
17 | 0 1 Terrain
18 | 1 1 Obstacle
19 | 2 1 Height
20 | 3 1 Flags
21 |
--------------------------------------------------------------------------------
/src/coursewindow.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include "coursewindow.h"
7 | #include "ui_coursewindow.h"
8 |
9 | #include "kirby.h"
10 | #include "level.h"
11 | #include "romfile.h"
12 |
13 | CourseWindow::CourseWindow(QWidget *parent) :
14 | QDialog(parent, Qt::CustomizeWindowHint
15 | | Qt::WindowTitleHint
16 | | Qt::WindowCloseButtonHint
17 | | Qt::MSWindowsFixedSizeDialogHint
18 | ),
19 | ui(new Ui::CourseWindow)
20 | {
21 | ui->setupUi(this);
22 |
23 | // prevent window resizing
24 | this->layout()->setSizeConstraint(QLayout::SetFixedSize);
25 | }
26 |
27 | CourseWindow::~CourseWindow()
28 | {
29 | delete ui;
30 | }
31 |
32 | int CourseWindow::select(int level, ROMFile::game_e game) {
33 | // add course names to dropdown
34 | ui->comboBox->clear();
35 | for (int i = 0; i < numLevels[game] / 8; i++)
36 | ui->comboBox->addItem(courseNames[game][i]);
37 |
38 | ui->comboBox->setCurrentIndex(level / 8);
39 | ui->spinBox->setValue((level % 8) + 1);
40 |
41 | if (this->exec())
42 | return ui->comboBox->currentIndex() * 8
43 | + ui->spinBox->value() - 1;
44 |
45 | return level;
46 | }
47 |
--------------------------------------------------------------------------------
/src/coursewindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef COURSEWINDOW_H
7 | #define COURSEWINDOW_H
8 |
9 | #include
10 | #include "level.h"
11 | #include "romfile.h"
12 |
13 | namespace Ui {
14 | class CourseWindow;
15 | }
16 |
17 | class CourseWindow : public QDialog
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | explicit CourseWindow(QWidget *parent = 0);
23 | ~CourseWindow();
24 |
25 | int select(int level, ROMFile::game_e game = ROMFile::kirby);
26 |
27 | private:
28 | Ui::CourseWindow *ui;
29 | };
30 |
31 | #endif // COURSEWINDOW_H
32 |
--------------------------------------------------------------------------------
/src/coursewindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | CourseWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 303
10 | 69
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | Select Course
21 |
22 |
23 | -
24 |
25 | -
26 |
27 |
28 |
29 | 0
30 | 0
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 | Hole
39 |
40 |
41 |
42 | -
43 |
44 |
45 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
46 |
47 |
48 | 1
49 |
50 |
51 | 8
52 |
53 |
54 |
55 |
56 |
57 | -
58 |
59 |
60 | Qt::Horizontal
61 |
62 |
63 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | buttonBox
73 | accepted()
74 | CourseWindow
75 | accept()
76 |
77 |
78 | 248
79 | 254
80 |
81 |
82 | 157
83 | 274
84 |
85 |
86 |
87 |
88 | buttonBox
89 | rejected()
90 | CourseWindow
91 | reject()
92 |
93 |
94 | 316
95 | 260
96 |
97 |
98 | 286
99 | 274
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/src/graphics.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef GRAPHICS_H
7 | #define GRAPHICS_H
8 |
9 | #define PAL(x) (x << 10)
10 | #define PALN(x) (x >> 10 & 0x7)
11 | #define TILE(x) (x & 0x3FF)
12 | #define PRI 0x2000
13 | #define FH 0x4000
14 | #define FV 0x8000
15 | #define FB FH|FV
16 |
17 | #define TILE_SIZE 64
18 | #define ISO_TILE_SIZE 8
19 |
20 | #endif // GRAPHICS_H
21 |
--------------------------------------------------------------------------------
/src/icons.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | images/icons/arrow_in.png
4 | images/icons/brick_edit.png
5 | images/icons/cut.png
6 | images/icons/delete.png
7 | images/icons/disk.png
8 | images/icons/door_in.png
9 | images/icons/folder.png
10 | images/icons/help.png
11 | images/icons/image.png
12 | images/icons/paste_plain.png
13 | images/icons/resultset_first.png
14 | images/icons/resultset_last.png
15 | images/icons/resultset_next.png
16 | images/icons/resultset_previous.png
17 | images/icons/wand.png
18 | images/icons/page_copy.png
19 | images/icons/magnifier.png
20 | images/icons/arrow_redo.png
21 | images/icons/arrow_undo.png
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/images.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | images/water.png
4 | images/terrain.png
5 | images/bounce.png
6 | images/traps.png
7 | images/conveyor.png
8 | images/kirby.png
9 | images/warps.png
10 | images/unknown.png
11 | images/gordo.png
12 | images/enemies.png
13 | images/rotate.png
14 | images/3dtiles.png
15 | images/movers.png
16 | images/bumpers.png
17 | images/3dtiles-water.png
18 | images/switches.png
19 | images/dedede.png
20 | images/gordo3d.png
21 | images/main16.png
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/images/3dtiles-water.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/3dtiles-water.png
--------------------------------------------------------------------------------
/src/images/3dtiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/3dtiles.png
--------------------------------------------------------------------------------
/src/images/bounce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/bounce.png
--------------------------------------------------------------------------------
/src/images/bumpers.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/bumpers.pdn
--------------------------------------------------------------------------------
/src/images/bumpers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/bumpers.png
--------------------------------------------------------------------------------
/src/images/conveyor.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/conveyor.pdn
--------------------------------------------------------------------------------
/src/images/conveyor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/conveyor.png
--------------------------------------------------------------------------------
/src/images/dedede.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/dedede.pdn
--------------------------------------------------------------------------------
/src/images/dedede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/dedede.png
--------------------------------------------------------------------------------
/src/images/enemies.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/enemies.pdn
--------------------------------------------------------------------------------
/src/images/enemies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/enemies.png
--------------------------------------------------------------------------------
/src/images/gordo.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/gordo.pdn
--------------------------------------------------------------------------------
/src/images/gordo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/gordo.png
--------------------------------------------------------------------------------
/src/images/gordo3d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/gordo3d.png
--------------------------------------------------------------------------------
/src/images/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/grid.png
--------------------------------------------------------------------------------
/src/images/icons/arrow_in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/arrow_in.png
--------------------------------------------------------------------------------
/src/images/icons/arrow_redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/arrow_redo.png
--------------------------------------------------------------------------------
/src/images/icons/arrow_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/arrow_undo.png
--------------------------------------------------------------------------------
/src/images/icons/brick_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/brick_edit.png
--------------------------------------------------------------------------------
/src/images/icons/cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/cut.png
--------------------------------------------------------------------------------
/src/images/icons/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/delete.png
--------------------------------------------------------------------------------
/src/images/icons/disk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/disk.png
--------------------------------------------------------------------------------
/src/images/icons/door_in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/door_in.png
--------------------------------------------------------------------------------
/src/images/icons/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/folder.png
--------------------------------------------------------------------------------
/src/images/icons/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/help.png
--------------------------------------------------------------------------------
/src/images/icons/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/image.png
--------------------------------------------------------------------------------
/src/images/icons/magnifier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/magnifier.png
--------------------------------------------------------------------------------
/src/images/icons/page_copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/page_copy.png
--------------------------------------------------------------------------------
/src/images/icons/paste_plain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/paste_plain.png
--------------------------------------------------------------------------------
/src/images/icons/resultset_first.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/resultset_first.png
--------------------------------------------------------------------------------
/src/images/icons/resultset_last.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/resultset_last.png
--------------------------------------------------------------------------------
/src/images/icons/resultset_next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/resultset_next.png
--------------------------------------------------------------------------------
/src/images/icons/resultset_previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/resultset_previous.png
--------------------------------------------------------------------------------
/src/images/icons/wand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/icons/wand.png
--------------------------------------------------------------------------------
/src/images/kirby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/kirby.png
--------------------------------------------------------------------------------
/src/images/main.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main.icns
--------------------------------------------------------------------------------
/src/images/main.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main.ico
--------------------------------------------------------------------------------
/src/images/main16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main16.png
--------------------------------------------------------------------------------
/src/images/main32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main32.png
--------------------------------------------------------------------------------
/src/images/main48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main48.png
--------------------------------------------------------------------------------
/src/images/main64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/main64.png
--------------------------------------------------------------------------------
/src/images/mainicon.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/mainicon.pdn
--------------------------------------------------------------------------------
/src/images/movers.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/movers.pdn
--------------------------------------------------------------------------------
/src/images/movers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/movers.png
--------------------------------------------------------------------------------
/src/images/rotate.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/rotate.pdn
--------------------------------------------------------------------------------
/src/images/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/rotate.png
--------------------------------------------------------------------------------
/src/images/switches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/switches.png
--------------------------------------------------------------------------------
/src/images/terrain.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/terrain.pdn
--------------------------------------------------------------------------------
/src/images/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/terrain.png
--------------------------------------------------------------------------------
/src/images/tiletemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/tiletemplate.png
--------------------------------------------------------------------------------
/src/images/traps.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/traps.pdn
--------------------------------------------------------------------------------
/src/images/traps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/traps.png
--------------------------------------------------------------------------------
/src/images/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/unknown.png
--------------------------------------------------------------------------------
/src/images/warps.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/warps.pdn
--------------------------------------------------------------------------------
/src/images/warps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/warps.png
--------------------------------------------------------------------------------
/src/images/water.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/water.pdn
--------------------------------------------------------------------------------
/src/images/water.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devinacker/kdceditor/52069434e7945bf5651bdd497b78aa08ab168778/src/images/water.png
--------------------------------------------------------------------------------
/src/kirby.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include "kirby.h"
7 | #include
8 |
9 | const int fgPaletteBase[] = {0xD4A9, 0xD8ED, 0xD8ED};
10 | const int waterBase[][3] = {{0xA444, 0xE030, 0xE030},
11 | {0xA46E, 0xE05A, 0xE05A}};
12 |
13 | const int paletteTable[] = {0x80D425, 0x80D869, 0x80D869};
14 | const int waterTable[][3] = {{0x8484AF, 0x8484AF, 0x8484AF},
15 | {0x8484F1, 0x8484F1, 0x8484F1}};
16 | const int backgroundTable[][3] = {{0x80D0AF, 0x80D517, 0x80D517},
17 | {0x80D304, 0x80D748, 0x80D748},
18 | {0x80D324, 0x80D768, 0x80D768},
19 | {0x84CD23, 0x84CD42, 0x84CD42}};
20 |
21 | const int musicTable[] = {0x80C533, 0x80C99D, 0x80C99D};
22 | const int newMusicAddr[] = {0x80F440, 0x80F950, 0x80F950};
23 |
24 | // first dimension is indexed by game number (0 = kirby, 1 = sts)
25 | const char* courseNames[][224 / 8] = {
26 | // KDC / Kirby Bowl courses
27 | {
28 | "1P Course 1",
29 | "1P Course 2",
30 | "1P Course 3",
31 | "1P Course 4",
32 | "1P Course 5",
33 | "1P Course 6",
34 | "1P Course 7",
35 | "1P Course 8",
36 | "1P Extra Course 1",
37 | "1P Extra Course 2",
38 | "1P Extra Course 3",
39 | "1P Extra Course 4",
40 | "1P Extra Course 5",
41 | "1P Extra Course 6",
42 | "1P Extra Course 7",
43 | "1P Extra Course 8",
44 | "2P Course 1",
45 | "2P Course 2",
46 | "2P Course 3",
47 | "2P Course 4",
48 | "Demo Course 1",
49 | "Demo Course 2",
50 | "Demo Course 3 / Test Course",
51 | "Test Course",
52 | "2P Extra Course 1",
53 | "2P Extra Course 2",
54 | "2P Extra Course 3",
55 | "2P Extra Course 4"
56 | },
57 | // Special Tee Shot courses
58 | {
59 | "Beginner Course",
60 | "Amateur Course",
61 | "Professional Course",
62 | "Master Course",
63 | "Extra Course 1",
64 | "Extra Course 2",
65 | "Extra Course 3",
66 | "Extra Course 4",
67 | "Gold Course"
68 | }
69 | };
70 |
71 | const bg_t bgNames[] = {
72 | {"Background 1 (clouds)",
73 | {0x8290, 0xC290, 0xC290},
74 | {0x92BEB1, 0x90A836, 0x90A836},
75 | {0x94AC83, 0x928000, 0x92855B},
76 | {0xCD33, 0xCD52, 0xCD52}},
77 |
78 | {"Background 2 (stars & moon)",
79 | {0x83D0, 0xC3D0, 0xC3D0},
80 | {0x92D18C, 0x90B1B2, 0x90B1B2},
81 | {0x94EDAB, 0x8EFB5F, 0x8EFB5F},
82 | {0xCECA, 0xCEE9, 0xCEE9}},
83 |
84 | {"Background 3 (waterfalls)",
85 | {0x8330, 0xC330, 0xC330},
86 | {0x93A043, 0x90ED83, 0x90ED83},
87 | {0x94967D, 0x91E7A2, 0x91EE62},
88 | {0xCE79, 0xCE98, 0xCE98}},
89 |
90 | {"Background 4 (jigsaw)",
91 | {0x82E0, 0xC2E0, 0xC2E0},
92 | {0x93E286, 0x91AD5B, 0x91B41B},
93 | {0x93D5F8, 0x91A0CD, 0x91A78D},
94 | {0xCE64, 0xCE83, 0xCE83}},
95 |
96 | {"Background 5 (candy)",
97 | {0x8380, 0xC380, 0xC380},
98 | {0x92AB0F, 0x909494, 0x909494},
99 | {0x93FA68, 0x91E20F, 0x91E8CF},
100 | {0xCFAE, 0xCFCD, 0xCFCD}},
101 |
102 | {"Background 6 (ocean)",
103 | {0x85E0, 0xC5E0, 0xC5E0},
104 | {0x9398A1, 0x90E5E1, 0x90E5E1},
105 | {0x94DA7C, 0x92B347, 0x92B8A2},
106 | {0xCFF3, 0xD012, 0xD012}}
107 | /*
108 | something here (the GFX?) gets loaded to a different address than normal
109 | so it's not usable with the rest of the course BGs
110 | {"Background 7 (diamonds)",
111 | {0xD368, 0},
112 | {0x93EEC0, 0},
113 | {0x9798EF, 0},
114 | {0xD12D, 0}},
115 | */
116 | };
117 |
118 | const char* paletteNames[] = {
119 | "Course 1 (blue)",
120 | "Course 2 (green)",
121 | "Course 3 (purple)",
122 | "Course 4 (pink)",
123 | "Course 5 (tan)",
124 | "Course 6 (beige)",
125 | "Course 7 (grey)",
126 | "Course 8 (red)",
127 | "Extra course 7/8 (dark grey)",
128 | "Demo course (teal)"
129 | };
130 |
131 | const StringMap musicNames ({
132 | {0x7e, "7E: (none)"},
133 | {0x80, "80: Epilogue"},
134 | {0x82, "82: Title"},
135 | {0x83, "83: Opening demo (JP only)"},
136 | {0x84, "84: High scores"},
137 | {0x85, "85: Space Valley (course 2/7b)"},
138 | {0x86, "86: Over Water (course 1b)"},
139 | {0x87, "87: The Tricky Stuff (course 5b)"},
140 | {0x88, "88: Castles of Cake (course 6)"},
141 | {0x89, "89: Green Fields (course 5a/7a)"},
142 | {0x8a, "8A: The First Hole (course 1a/3)"},
143 | {0x8b, "8B: Iceberg Ocean (course 8)"},
144 | {0x8c, "8C: Last Hole"},
145 | {0x8d, "8D: Jigsaw Plains (course 4)"},
146 | {0x8f, "8F: Continue?"},
147 | {0x92, "92: Final score"},
148 | {0x93, "93: 2P course select"},
149 | {0x94, "94: Eyecatch"},
150 | {0x95, "95: Main menu"},
151 | {0x96, "96: 1P course select"},
152 | {0x97, "97: Scorecard"},
153 | {0x9a, "9A: Demo play"},
154 | {0x9b, "9B: Dedede 1"},
155 | {0x9c, "9C: Dedede 2"},
156 | {0x9f, "9F: Game over"}
157 | });
158 |
159 | const StringMap kirbyGeometry ({
160 | {0, "00: None"},
161 | {1, "01: Flat"},
162 | {2, "02: Four slopes up towards center"},
163 |
164 | // this type is unusable
165 | // {3, "03: Four slopes down into ground"},
166 |
167 | {4, "04: Slope down towards south"},
168 | {5, "05: Slope down towards east"},
169 | {6, "06: Slope down towards north"},
170 | {7, "07: Slope down towards west"},
171 |
172 | {8, "08: Slopes down towards south and east (inner)"},
173 | {9, "09: Slopes down towards north and east (inner)"},
174 | {10, "0A: Slopes down towards north and west (inner)"},
175 | {11, "0B: Slopes down towards south and west (inner)"},
176 |
177 | {12, "0C: Slopes down towards south and east (outer)"},
178 | {13, "0D: Slopes down towards north and east (outer)"},
179 | {14, "0E: Slopes down towards north and west (outer)"},
180 | {15, "0F: Slopes down towards south and west (outer)"},
181 |
182 | {16, "10: Slope down towards southeast (top)"},
183 | {17, "11: Slope down towards northeast (top)"},
184 | {18, "12: Slope down towards northwest (top)"},
185 | {19, "13: Slope down towards southwest (top)"},
186 |
187 | {20, "14: Slope down towards southeast (bottom)"},
188 | {21, "15: Slope down towards northeast (bottom)"},
189 | {22, "16: Slope down towards northwest (bottom)"},
190 | {23, "17: Slope down towards southwest (bottom)"},
191 |
192 | {24, "18: Slope down towards southeast (middle)"},
193 | {25, "19: Slope down towards northeast (middle)"},
194 | {26, "1A: Slope down towards northwest (middle)"},
195 | {27, "1B: Slope down towards southwest (middle)"}
196 | });
197 |
198 | const StringMap kirbyObstacles ({
199 | {0x00, "00: None"},
200 |
201 | {0x02, "02: Whispy Woods"},
202 |
203 | {0x04, "04: Sand trap"},
204 | {0x05, "05: Spike pit"},
205 |
206 | /*
207 | hole can't be placed independently in KDC
208 | (unless it can be placed on obstacle layer w/ correct palette somehow)
209 | {0x08, "08: Hole"},
210 | */
211 |
212 | {0x0c, "0C: Kirby"},
213 | {0x0d, "0D: King Dedede (course 24-1 only)"},
214 |
215 | {0x10, "10: Current (south)"},
216 | {0x11, "11: Current (east)"},
217 | {0x12, "12: Current (north)"},
218 | {0x13, "13: Current (west)"},
219 |
220 | {0x14, "14: Arrow (south)"},
221 | {0x15, "15: Arrow (east)"},
222 | {0x16, "16: Arrow (north)"},
223 | {0x17, "17: Arrow (west)"},
224 |
225 | {0x18, "18: Booster (south)"},
226 | {0x19, "19: Booster (east)"},
227 | {0x1a, "1A: Booster (north)"},
228 | {0x1b, "1B: Booster (west)"},
229 |
230 | {0x1c, "1C: Air vent (north-south)"},
231 | {0x1d, "1D: Air vent (east-west)"},
232 |
233 | {0x20, "20: Bounce (use with tile 04)"},
234 | {0x21, "21: Bounce (use with tile 05)"},
235 | {0x22, "22: Bounce (use with tile 06)"},
236 | {0x23, "23: Bounce (use with tile 07)"},
237 | {0x24, "24: Bounce"},
238 |
239 | {0x28, "28: Bumper (north to south)"},
240 | {0x29, "29: Bumper (east to west)"},
241 | {0x2a, "2A: Bumper (south to west)"},
242 | {0x2b, "2B: Bumper (north to west)"},
243 | {0x2c, "2C: Bumper (north to east)"},
244 | {0x2d, "2D: Bumper (south to east)"},
245 |
246 | {0x30, "30: Conveyor belt (south)"},
247 | {0x31, "31: Conveyor belt (east)"},
248 | {0x32, "32: Conveyor belt (north)"},
249 | {0x33, "33: Conveyor belt (west)"},
250 |
251 | {0x34, "34: Conveyor belt (north, use with tile 04)"},
252 | {0x35, "35: Conveyor belt (south, use with tile 04)"},
253 | {0x36, "36: Conveyor belt (west, use with tile 05)"},
254 | {0x37, "37: Conveyor belt (east, use with tile 05)"},
255 | {0x38, "38: Conveyor belt (south, use with tile 06)"},
256 | {0x39, "39: Conveyor belt (north, use with tile 06)"},
257 | {0x3a, "3A: Conveyor belt (east, use with tile 07)"},
258 | {0x3b, "3B: Conveyor belt (west, use with tile 07)"},
259 |
260 | {0x40, "40: Waddle Dee"},
261 | {0x41, "41: Rocky"},
262 | {0x42, "42: Waddle Doo"},
263 | {0x43, "43: Flamer"},
264 | {0x44, "44: Spiney"},
265 | {0x45, "45: Twister"},
266 | {0x46, "46: Wheelie"},
267 | {0x47, "47: Sparky"},
268 | {0x48, "48: Starman"},
269 | {0x49, "49: Chilly"},
270 | {0x4a, "4A: Broom Hatter"},
271 | {0x4b, "4B: Squishy"},
272 | {0x4c, "4C: Kabu"},
273 | {0x4d, "4D: Gaspar"},
274 | {0x4e, "4E: Pumpkin"},
275 | {0x4f, "4F: UFO"},
276 | {0x50, "50: Gaspar (higher)"},
277 | {0x51, "51: Pumpkin (higher)"},
278 | {0x52, "52: UFO (higher)"},
279 | {0x57, "57: Transformer"},
280 |
281 | {0x58, "58: Mr. Bright switch"},
282 | {0x59, "59: Mr. Shine switch"},
283 | {0x5a, "5A: Rotating space switch (off)"},
284 | {0x5b, "5B: Rotating space switch (on)"},
285 | {0x5c, "5C: Water switch (on)"},
286 | {0x5d, "5D: Water switch (off)"},
287 |
288 | {0x61, "61: Water hazard"},
289 | {0x64, "64: Water hazard (use with tile 04)"},
290 | {0x65, "65: Water hazard (use with tile 05)"},
291 | {0x66, "66: Water hazard (use with tile 06)"},
292 | {0x67, "67: Water hazard (use with tile 07)"},
293 | {0x68, "68: Water hazard (use with tile 08)"},
294 | {0x69, "69: Water hazard (use with tile 09)"},
295 | {0x6a, "6A: Water hazard (use with tile 0A)"},
296 | {0x6b, "6B: Water hazard (use with tile 0B)"},
297 | {0x6c, "6C: Water hazard (use with tile 0C)"},
298 | {0x6d, "6D: Water hazard (use with tile 0D)"},
299 | {0x6e, "6E: Water hazard (use with tile 0E)"},
300 | {0x6f, "6F: Water hazard (use with tile 0F)"},
301 |
302 | /*
303 | Most of these are not used in KDC.
304 | */
305 | {0x70, "70: Rotating space (clockwise, always on)"},
306 | {0x71, "71: Rotating space (counterclockwise, always on)"},
307 | {0x72, "72: Rotating space (clockwise, always on, slow)"},
308 | {0x73, "73: Rotating space (counterclockwise, always on, slow)"},
309 | {0x74, "74: Rotating space (clockwise, switch)"},
310 | {0x75, "75: Rotating space (counterclockwise, switch)"},
311 | {0x76, "76: Rotating space (clockwise, switch, slow)"},
312 | {0x77, "77: Rotating space (counterclockwise, switch, slow)"},
313 | {0x78, "78: Rotating space (clockwise, switch-opposite)"},
314 | {0x79, "79: Rotating space (counterclockwise, switch-opposite)"},
315 | {0x7a, "7A: Rotating space (clockwise, switch-opposite, slow)"},
316 | {0x7b, "7B: Rotating space (counterclockwise, switch-opposite, slow)"},
317 |
318 | {0x80, "80: Gordo (moves south, faces south)"},
319 | {0x81, "81: Gordo (moves east, faces south)"},
320 | {0x82, "82: Gordo (moves north, faces south)"},
321 | {0x83, "83: Gordo (moves west, faces south)"},
322 | {0x84, "84: Gordo (moves south, faces east)"},
323 | {0x85, "85: Gordo (moves east, faces east)"},
324 | {0x86, "86: Gordo (moves north, faces east)"},
325 | {0x87, "87: Gordo (moves west, faces east)"},
326 | {0x88, "88: Gordo (moves south, faces north)"},
327 | {0x89, "89: Gordo (moves east, faces north)"},
328 | {0x8a, "8A: Gordo (moves north, faces north)"},
329 | {0x8b, "8B: Gordo (moves west, faces north)"},
330 | {0x8c, "8C: Gordo (moves south, faces west)"},
331 | {0x8d, "8D: Gordo (moves east, faces west)"},
332 | {0x8e, "8E: Gordo (moves north, faces west)"},
333 | {0x8f, "8F: Gordo (moves west, faces west)"},
334 |
335 | {0x90, "90: Gordo (moves up/down, faces south)"},
336 | {0x91, "91: Gordo (moves up/down, faces east)"},
337 | {0x92, "92: Gordo (moves up/down, faces north)"},
338 | {0x93, "93: Gordo (moves up/down, faces west)"},
339 | {0x94, "94: Gordo (moves down/up, faces south)"},
340 | {0x95, "95: Gordo (moves down/up, faces east)"},
341 | {0x96, "96: Gordo (moves down/up, faces north)"},
342 | {0x97, "97: Gordo (moves down/up, faces west)"},
343 |
344 | {0x98, "98: Gordo path (north-south)"},
345 | {0x99, "99: Gordo path (east-west)"},
346 | {0x9a, "9A: Gordo path (northwest corner)"},
347 | {0x9b, "9B: Gordo path (southwest corner)"},
348 | {0x9c, "9C: Gordo path (southeast corner)"},
349 | {0x9d, "9D: Gordo path (northeast corner)"},
350 | {0x9e, "9E: Gordo endpoint (south)"},
351 | {0x9f, "9F: Gordo endpoint (east)"},
352 | {0xa0, "A0: Gordo endpoint (north)"},
353 | {0xa1, "A1: Gordo endpoint (west)"},
354 |
355 | {0xac, "AC: Kracko (no lightning)"},
356 | {0xad, "AD: Kracko (lightning 1)"},
357 | {0xae, "AE: Kracko (lightning 2)"},
358 |
359 | {0xb0, "B0: Blue warp 1 (south)"},
360 | {0xb1, "B1: Blue warp 1 (east)"},
361 | {0xb2, "B2: Blue warp 1 (north)"},
362 | {0xb3, "B3: Blue warp 1 (west)"},
363 | {0xb4, "B4: Blue warp 2 (south)"},
364 | {0xb5, "B5: Blue warp 2 (east)"},
365 | {0xb6, "B6: Blue warp 2 (north)"},
366 | {0xb7, "B7: Blue warp 2 (west)"},
367 | {0xb8, "B8: Red warp 1"},
368 | {0xb9, "B9: Red warp 2"},
369 |
370 | {0xc0, "C0: Starting line (west end)"},
371 | {0xc1, "C1: Starting line"},
372 | {0xc2, "C2: Starting line (east end)"},
373 | {0xc3, "C3: Kirby (course 24-1 only)"},
374 | });
375 |
--------------------------------------------------------------------------------
/src/kirby.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef KIRBY_H
7 | #define KIRBY_H
8 |
9 | #define KIRBY_GEOM_TYPES 0x1C
10 | #define KIRBY_OBSTACLE_TYPES 0xC4
11 | #define NUM_FG_PALETTES 10
12 | #define FG_PALETTE_SIZE 0x20
13 | #define WATER_PALETTE_SIZE 0x120
14 | #define BG_PALETTE_SIZE 0x28
15 | #define NUM_BACKGROUNDS 6
16 |
17 | #include
18 | #include
19 |
20 | typedef std::map StringMap;
21 |
22 | typedef struct {
23 | char name[50];
24 | int palette[3];
25 | int pointer1[3];
26 | int pointer2[3];
27 | int anim[3];
28 | } bg_t;
29 |
30 | extern const int fgPaletteBase[3];
31 | extern const int waterBase[2][3];
32 | extern const int bgPaletteBase[3];
33 |
34 | extern const int paletteTable[3];
35 | extern const int waterTable[2][3];
36 | extern const int backgroundTable[4][3];
37 | extern const int musicTable[3];
38 | extern const int newMusicAddr[3];
39 |
40 | extern const char* courseNames[][224 / 8];
41 | extern const bg_t bgNames[];
42 | extern const char* paletteNames[];
43 | extern const StringMap musicNames;
44 |
45 | extern const StringMap kirbyGeometry;
46 | extern const StringMap kirbyObstacles;
47 |
48 | #endif // KIRBY_H
49 |
--------------------------------------------------------------------------------
/src/level.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef LEVEL_H
7 | #define LEVEL_H
8 |
9 | #include "romfile.h"
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #define CHUNK_SIZE 2048
18 | #define BIG_CHUNK_SIZE 26624
19 |
20 | #define MAX_2D_AREA CHUNK_SIZE
21 | #define MAX_3D_AREA (BIG_CHUNK_SIZE / 2)
22 |
23 | // these are the limits for length/width and height of levels!
24 | // (for individual dimensions only; see the above two #defines too)
25 | #define MAX_2D_SIZE 100
26 | #define MAX_HEIGHT 49
27 |
28 | // maximum size of the 8x8 tile maps based on 2D map size
29 | // (with a bit of padding at the bottom just because)
30 | #define MAX_FIELD_HEIGHT (2 * (MAX_HEIGHT + MAX_2D_SIZE + MAX_2D_SIZE + 2))
31 | #define MAX_FIELD_WIDTH (8 * MAX_2D_SIZE)
32 |
33 | extern const int numLevels[];
34 | extern const int clippingTable[];
35 |
36 | // location of where to write new level data
37 | extern const uint newDataAddress[];
38 |
39 | /*
40 | The level header. Some fields currently unknown.
41 | Most of this should be generated automatically by the editor.
42 | */
43 | #pragma pack(1)
44 | typedef struct {
45 | uint16_t dummy1;
46 | uint16_t width, length;
47 | uint16_t dummy2;
48 | uint16_t fieldWidth, fieldHeight;
49 | uint16_t alignHoriz, alignVert;
50 | char mapID[12];
51 | } header_t;
52 |
53 | /*
54 | Level tile info (in chunks 1 to 4).
55 | */
56 | typedef struct {
57 | uint8_t geometry, obstacle, height;
58 | struct {
59 | unsigned bumperSouth: 1;
60 | unsigned bumperEast: 1;
61 | unsigned bumperNorth: 1;
62 | unsigned bumperWest: 1;
63 | unsigned dummy: 3;
64 | unsigned layer: 1;
65 | } flags;
66 | } maptile_t;
67 |
68 | /*
69 | Z-clipping hash table entry (in chunk 10).
70 | */
71 | typedef struct {
72 | uint8_t xLower, xUpper, prio;
73 | uint16_t zref;
74 | } clip_t;
75 | #pragma pack()
76 |
77 | extern const maptile_t noTile;
78 |
79 | /*
80 | Level tile info as it is passed to/from the tile edit window
81 | (and possibly a "copy/paste tile properties" feature in the future.)
82 | */
83 | typedef struct {
84 | int geometry, obstacle, height,
85 | bumperSouth, bumperEast, bumperNorth, bumperWest,
86 | layer, minHeight, maxHeight;
87 | } tileinfo_t;
88 |
89 | /*
90 | Definition for level data.
91 | Currently consists of tile and obstacle data and flags, as well as modified state
92 | (both overall and for the current session) and music track.
93 | */
94 | typedef struct {
95 | header_t header;
96 |
97 | // The maximum area of a map is 2048 maptiles. 64 is the maximum
98 | // size of either dimension needed to fit all of the original levels
99 | // (but the maximum size of each dimension depends on the size of the other,
100 | // so that width/length is always <= 2048.
101 | maptile_t tiles[MAX_2D_SIZE][MAX_2D_SIZE];
102 |
103 | // have any of the tile data fields been changed from the original data?
104 | // (determined based on their position in the ROM file, also set as soon
105 | // as level is edited)
106 | bool modified;
107 | // have any of the tile data fields been changed in this session?
108 | // (set when modified, cleared when saved)
109 | bool modifiedRecently;
110 |
111 | // music track number. see kirby.cpp (or stuff.cpp if i ever rename it)
112 | uint8_t music;
113 | } leveldata_t;
114 |
115 | /*
116 | Functions for loading/saving level data
117 | */
118 | leveldata_t* loadLevel(ROMFile& file, uint num);
119 | QList saveLevel(leveldata_t *level, int *fieldSize = 0);
120 | uint saveAllLevels(ROMFile& file, leveldata_t **levels);
121 |
122 | size_t makeClipTable(const leveldata_t *level, uint8_t *buffer);
123 | void makeIsometricMap(uint16_t playfield[2][MAX_FIELD_HEIGHT][MAX_FIELD_WIDTH], leveldata_t *level);
124 |
125 | uint levelHeight(const leveldata_t *level);
126 | bool waterLevel(const leveldata_t *level);
127 |
128 | /*
129 | * Worker objects for generating compressed level data
130 | */
131 | class SaveWorker : public QRunnable {
132 |
133 | public:
134 | SaveWorker(leveldata_t *level)
135 | : level(level) {
136 | this->setAutoDelete(false);
137 | QThreadPool::globalInstance()->start(this);
138 | }
139 |
140 | ~SaveWorker() {
141 | for (QByteArray*& chunk: chunks) {
142 | delete chunk;
143 | }
144 | }
145 |
146 | void run() {
147 | chunks = saveLevel(level, &fieldSize);
148 | }
149 |
150 | const QList& getChunks() const {
151 | QThreadPool::globalInstance()->waitForDone();
152 | return chunks;
153 | }
154 |
155 | int getFieldSize() const {
156 | QThreadPool::globalInstance()->waitForDone();
157 | return fieldSize;
158 | }
159 |
160 | protected:
161 | QList chunks;
162 | leveldata_t *level;
163 | int fieldSize;
164 |
165 | };
166 |
167 | #endif // LEVEL_H
168 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "mainwindow.h"
3 | #include "version.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | QApplication a(argc, argv);
8 |
9 | a.setApplicationName(INFO_NAME);
10 | a.setApplicationVersion(INFO_VERS);
11 |
12 | MainWindow w;
13 | w.show();
14 |
15 | return a.exec();
16 | }
17 |
--------------------------------------------------------------------------------
/src/mainwindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef MAINWINDOW_H
7 | #define MAINWINDOW_H
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "romfile.h"
15 | #include "mapscene.h"
16 | #include "level.h"
17 | #include "previewwindow.h"
18 |
19 | namespace Ui {
20 | class MainWindow;
21 | }
22 |
23 | class MainWindow : public QMainWindow
24 | {
25 | Q_OBJECT
26 |
27 | public:
28 | explicit MainWindow(QWidget *parent = 0);
29 | ~MainWindow();
30 |
31 | protected slots:
32 | // file menu
33 | void openFile();
34 | void saveFile();
35 | void saveFileAs();
36 | int closeFile();
37 |
38 | void setUnsaved();
39 |
40 | // level menu
41 | void loadLevelFromFile();
42 | void saveLevelToFile();
43 | void loadCourseFromFile();
44 | void saveCourseToFile();
45 |
46 | void saveCurrentLevel();
47 |
48 | void levelProperties();
49 |
50 | void selectCourse();
51 | void prevLevel();
52 | void nextLevel();
53 | void prevCourse();
54 | void nextCourse();
55 |
56 | // help menu
57 | void showHelp();
58 | void showAbout();
59 |
60 | // display text on the statusbar
61 | void status(const QString &msg);
62 |
63 | // debug menu crap
64 | void dumpLevel();
65 |
66 | // toolbar updates
67 | void setOpenFileActions(bool val);
68 | void setEditActions(bool val);
69 | void setUndoRedoActions(bool val = true);
70 | void setLevelChangeActions(bool val);
71 |
72 | protected:
73 | void closeEvent(QCloseEvent *);
74 |
75 | private:
76 | Ui::MainWindow *ui;
77 |
78 | QSettings *settings;
79 |
80 | // Information about the currently open file
81 | QString fileName;
82 | ROMFile rom;
83 | bool fileOpen, unsaved, saving;
84 |
85 | // The level data (28 courses, 8 holes each)
86 | int level;
87 | leveldata_t* levels[224];
88 | leveldata_t currentLevel;
89 | QLabel *levelLabel;
90 |
91 | // course background/palette settings
92 | // (background selectiosn repeat every 8 courses)
93 | int background[8];
94 | int palette[28];
95 | int waterPalette[28];
96 |
97 | // renderin stuff
98 | MapScene *scene;
99 | // the preview window
100 | PreviewWindow *previewWin;
101 |
102 | // various funcs
103 | void setupSignals();
104 | void setupActions();
105 | void getSettings();
106 | void saveSettings();
107 | void updateTitle();
108 | void setLevel(int);
109 | QMessageBox::StandardButton checkSaveLevel();
110 | QMessageBox::StandardButton checkSaveROM();
111 | };
112 |
113 | #endif // MAINWINDOW_H
114 |
--------------------------------------------------------------------------------
/src/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 800
10 | 600
11 |
12 |
13 |
14 |
15 | 640
16 | 480
17 |
18 |
19 |
20 | Kirby's Dream Course Editor
21 |
22 |
23 |
24 | :/images/main16.png :/images/main16.png
25 |
26 |
27 | 1.000000000000000
28 |
29 |
30 |
31 |
32 | 0
33 | 0
34 |
35 |
36 |
37 | -
38 |
39 |
40 | Qt::ScrollBarAlwaysOn
41 |
42 |
43 | Qt::ScrollBarAlwaysOn
44 |
45 |
46 | true
47 |
48 |
49 | Qt::AlignCenter
50 |
51 |
52 |
53 |
54 | 0
55 | 0
56 | 763
57 | 510
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
143 |
144 |
145 |
146 | toolBar
147 |
148 |
149 | true
150 |
151 |
152 | Qt::BottomToolBarArea|Qt::TopToolBarArea
153 |
154 |
155 | false
156 |
157 |
158 | TopToolBarArea
159 |
160 |
161 | false
162 |
163 |
164 |
165 |
166 |
167 | :/images/icons/folder.png :/images/icons/folder.png
168 |
169 |
170 | &Open ROM...
171 |
172 |
173 | Ctrl+O
174 |
175 |
176 |
177 |
178 |
179 | :/images/icons/disk.png :/images/icons/disk.png
180 |
181 |
182 | &Save ROM
183 |
184 |
185 | Ctrl+S
186 |
187 |
188 |
189 |
190 | Save ROM As...
191 |
192 |
193 | Ctrl+Shift+S
194 |
195 |
196 |
197 |
198 | Close ROM
199 |
200 |
201 |
202 |
203 | true
204 |
205 |
206 | Load Course from &File...
207 |
208 |
209 |
210 |
211 | true
212 |
213 |
214 | Save Level...
215 |
216 |
217 |
218 |
219 | true
220 |
221 |
222 | Save Course to File...
223 |
224 |
225 |
226 |
227 | E&xit
228 |
229 |
230 | Ctrl+Q
231 |
232 |
233 |
234 |
235 | true
236 |
237 |
238 |
239 | :/images/icons/cut.png :/images/icons/cut.png
240 |
241 |
242 | Cu&t
243 |
244 |
245 | Ctrl+X
246 |
247 |
248 |
249 |
250 | true
251 |
252 |
253 |
254 | :/images/icons/page_copy.png :/images/icons/page_copy.png
255 |
256 |
257 | &Copy
258 |
259 |
260 | Ctrl+C
261 |
262 |
263 |
264 |
265 | true
266 |
267 |
268 |
269 | :/images/icons/paste_plain.png :/images/icons/paste_plain.png
270 |
271 |
272 | &Paste
273 |
274 |
275 | Ctrl+V
276 |
277 |
278 |
279 |
280 | true
281 |
282 |
283 |
284 | :/images/icons/delete.png :/images/icons/delete.png
285 |
286 |
287 | &Delete
288 |
289 |
290 | Del
291 |
292 |
293 |
294 |
295 | true
296 |
297 |
298 |
299 | :/images/icons/help.png :/images/icons/help.png
300 |
301 |
302 | &Contents...
303 |
304 |
305 | F1
306 |
307 |
308 |
309 |
310 | &About...
311 |
312 |
313 |
314 |
315 | true
316 |
317 |
318 |
319 | :/images/icons/brick_edit.png :/images/icons/brick_edit.png
320 |
321 |
322 | &Edit Tiles...
323 |
324 |
325 | E
326 |
327 |
328 |
329 |
330 |
331 | :/images/icons/resultset_previous.png :/images/icons/resultset_previous.png
332 |
333 |
334 | Previous Level
335 |
336 |
337 | Ctrl+Left
338 |
339 |
340 |
341 |
342 |
343 | :/images/icons/resultset_next.png :/images/icons/resultset_next.png
344 |
345 |
346 | Next Level
347 |
348 |
349 | Ctrl+Right
350 |
351 |
352 |
353 |
354 |
355 | :/images/icons/resultset_first.png :/images/icons/resultset_first.png
356 |
357 |
358 | Previous Course
359 |
360 |
361 | Ctrl+Shift+Left
362 |
363 |
364 |
365 |
366 |
367 | :/images/icons/resultset_last.png :/images/icons/resultset_last.png
368 |
369 |
370 | Next Course
371 |
372 |
373 | Ctrl+Shift+Right
374 |
375 |
376 |
377 |
378 |
379 | :/images/icons/wand.png :/images/icons/wand.png
380 |
381 |
382 | Level Properties
383 |
384 |
385 | Ctrl+P
386 |
387 |
388 |
389 |
390 | Dump level header to stdout
391 |
392 |
393 | true
394 |
395 |
396 |
397 |
398 | Dump level chunks to text file
399 |
400 |
401 |
402 |
403 |
404 | :/images/icons/magnifier.png :/images/icons/magnifier.png
405 |
406 |
407 | Show Preview
408 |
409 |
410 |
411 |
412 | true
413 |
414 |
415 | true
416 |
417 |
418 |
419 | :/images/icons/arrow_in.png :/images/icons/arrow_in.png
420 |
421 |
422 | Center Preview
423 |
424 |
425 |
426 |
427 |
428 | :/images/icons/image.png :/images/icons/image.png
429 |
430 |
431 | Save Level to Image...
432 |
433 |
434 |
435 |
436 |
437 | :/images/icons/door_in.png :/images/icons/door_in.png
438 |
439 |
440 | Select Course...
441 |
442 |
443 |
444 |
445 |
446 | :/images/icons/arrow_undo.png :/images/icons/arrow_undo.png
447 |
448 |
449 | Undo
450 |
451 |
452 | Ctrl+Z
453 |
454 |
455 |
456 |
457 |
458 | :/images/icons/arrow_redo.png :/images/icons/arrow_redo.png
459 |
460 |
461 | Redo
462 |
463 |
464 | Ctrl+Y
465 |
466 |
467 |
468 |
469 | Load Level from File...
470 |
471 |
472 |
473 |
474 | Save Level to File...
475 |
476 |
477 |
478 |
479 | Raise Tiles
480 |
481 |
482 | =
483 |
484 |
485 |
486 |
487 | Lower Tiles
488 |
489 |
490 | -
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
--------------------------------------------------------------------------------
/src/mapchange.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include "mapchange.h"
7 | #include "level.h"
8 |
9 | MapChange::MapChange(leveldata_t *currLevel, uint selX, uint selY, uint selW, uint selL, QUndoCommand *parent) :
10 | QUndoCommand(parent),
11 | level(currLevel),
12 | x(selX), y(selY), w(selW), l(selL),
13 | before(new maptile_t[l * w]),
14 | after (new maptile_t[l * w]),
15 | first(true)
16 | {
17 | // when instantiated, save the region's pre-edit state
18 | if (level) {
19 | for (uint row = 0; row < l; row++)
20 | for (uint col = 0; col < w; col++)
21 | before[(row * w) + col] = this->level->tiles[y + row][x + col];
22 |
23 | this->setText("edit");
24 | }
25 | }
26 |
27 | MapChange::~MapChange() {
28 | delete[] before;
29 | delete[] after;
30 | }
31 |
32 | void MapChange::undo() {
33 | // restore to the region's pre-edit state
34 | if (level) {
35 | for (uint row = 0; row < this->l; row++)
36 | for (uint col = 0; col < this->w; col++)
37 | this->level->tiles[y + row][x + col] = before[(row * w) + col];
38 | }
39 | }
40 |
41 | void MapChange::redo() {
42 | // if being pushed, save the region's post-edit state
43 | // otherwise restore it
44 | if (level) {
45 | for (uint row = 0; row < this->l; row++)
46 | for (uint col = 0; col < this->w; col++) {
47 | if (first) after[(row * w) + col] = this->level->tiles[y + row][x + col];
48 | else this->level->tiles[y + row][x + col] = after[(row * w) + col];
49 | }
50 |
51 | if (first) {
52 | level->modified = true;
53 | level->modifiedRecently = true;
54 | }
55 | }
56 |
57 | first = false;
58 | }
59 |
60 | void MapChange::setText(const QString &text) {
61 | QUndoCommand::setText(QString(text)
62 | .append(" from (%1, %2) to (%3, %4)")
63 | .arg(x).arg(y).arg(x + w - 1).arg(y + l - 1));
64 | }
65 |
--------------------------------------------------------------------------------
/src/mapchange.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef MAPCHANGE_H
7 | #define MAPCHANGE_H
8 |
9 | #include
10 |
11 | #include "level.h"
12 |
13 | class MapChange : public QUndoCommand
14 | {
15 | public:
16 | explicit MapChange(leveldata_t *currLevel,
17 | uint x, uint y, uint w, uint l,
18 | QUndoCommand *parent = 0);
19 | ~MapChange();
20 |
21 | void undo();
22 | void redo();
23 | void setText(const QString &text);
24 |
25 | private:
26 | leveldata_t *level;
27 | uint x, y, w, l;
28 | maptile_t *before, *after;
29 | bool first;
30 | };
31 |
32 | #endif // MAPCHANGE_H
33 |
--------------------------------------------------------------------------------
/src/mapscene.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef MAPSCENE_H
7 | #define MAPSCENE_H
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "level.h"
14 |
15 | // subclass of QGraphicsScene used to draw the 2d map and handle mouse/kb events for it
16 | class MapScene : public QWidget {
17 | Q_OBJECT
18 |
19 | private:
20 | static const QColor infoColor, infoBackColor;
21 | static const QColor selectionColor, selectionBorder;
22 | static const QColor layerColor;
23 | static const QFont infoFont;
24 | static const QFontMetrics infoFontMetrics;
25 |
26 | int tileX, tileY;
27 | int selX, selY, selLength, selWidth;
28 | bool selecting;
29 |
30 | maptile_t copyBuffer[MAX_2D_SIZE][MAX_2D_SIZE];
31 | uint copyWidth, copyLength;
32 |
33 | QUndoStack stack;
34 |
35 | leveldata_t *level;
36 |
37 | //QGraphicsPixmapItem *infoItem, *selectionItem;
38 |
39 | QPixmap tiles, kirby, enemies, traps, bounce, movers, rotate,
40 | conveyor, bumpers, water, warps, gordo, switches, dedede,
41 | unknown;
42 |
43 | void copyTiles(bool cut);
44 | void showTileInfo(QMouseEvent *event);
45 | void beginSelection(QMouseEvent *event);
46 | void updateSelection(QMouseEvent *event = NULL);
47 |
48 | public:
49 | explicit MapScene(QWidget *parent = 0, leveldata_t *currentLevel = 0);
50 |
51 | bool canUndo() const;
52 | bool canRedo() const;
53 | bool isClean() const;
54 |
55 | void cancelSelection();
56 |
57 | public slots:
58 | void editTiles();
59 | void undo();
60 | void redo();
61 | void clearStack();
62 | void setClean();
63 | void cut();
64 | void copy();
65 | void paste();
66 | void deleteTiles();
67 | void raiseTiles();
68 | void lowerTiles();
69 | void refresh(bool keepMouse = true);
70 |
71 | signals:
72 | void doubleClicked();
73 | void statusMessage(QString);
74 | void mouseOverTile(int x, int y);
75 | void edited();
76 |
77 | protected:
78 | void mouseMoveEvent(QMouseEvent *event);
79 | void mousePressEvent(QMouseEvent *event);
80 | void mouseReleaseEvent(QMouseEvent *event);
81 | void mouseDoubleClickEvent(QMouseEvent *event);
82 | void wheelEvent(QWheelEvent *event);
83 | void paintEvent(QPaintEvent *event);
84 | };
85 |
86 | #endif // MAPSCENE_H
87 |
--------------------------------------------------------------------------------
/src/metatile.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | metatile.cpp
3 |
4 | Contains code for generating metatiles - the individual parts of the isometric tilemap
5 | corresponding to individual tiles on the 2D map, based on a tile's terrain (or obstacle) and the
6 | ones to the north and west of it.
7 |
8 | Metatiles are stored as 8x8 arrays of tiles, where the first 4 columns represent a tile and its
9 | connection to the west (if any), and the other 4 represent its connection to the north.
10 |
11 | For (most of) the actual tile data, see the other metatile_*.cpp files.
12 |
13 | This code is released under the terms of the MIT license.
14 | See COPYING.txt for details.
15 |
16 | */
17 |
18 | #include "metatile.h"
19 | #include "graphics.h"
20 |
21 | using namespace stuff;
22 |
23 | // These tables map halves/edges of some terrain types to other types.
24 | // This is done when a terrain type (mainly two-way slopes or diagonal slope top/bottom parts)
25 | // can be drawn using parts of other types.
26 | const stuff::type_e trueLeftTable[KIRBY_GEOM_TYPES] = {
27 | // nothing, flat, slopes up, slopes down
28 | nothing, flat, slopeEast, slopeSouthAndWestInner,
29 | // cardinal slopes
30 | slopeSouth, slopeEast, slopeNorth, slopeWest,
31 | // inner slopes (SE, NE, NW, SW)
32 | slopeSouth, slopeNorth, slopeWest, slopeSouthAndWestInner,
33 | // outer slopes
34 | slopeEast, slopeEast, slopeNorth, slopeSouthAndWestOuter,
35 | // diagonal slopes upper
36 | slopeSoutheastFull, slopeNortheastFull, flat, flat,
37 | // diagonal slopes lower
38 | flat, flat, flat, slopeSouthwestFull,
39 | // diagonal slopes full
40 | slopeSoutheastFull, slopeNortheastFull, slopeNorthwestFull, slopeSouthwestFull
41 | };
42 |
43 | const stuff::type_e trueRightTable[KIRBY_GEOM_TYPES] = {
44 | // nothing, flat, slopes up, slopes down
45 | nothing, flat, slopeSouth, slopeNorthAndEastInner,
46 | // cardinal slopes
47 | slopeSouth, slopeEast, slopeNorth, slopeWest,
48 | // inner slopes (SE, NE, NW, SW)
49 | slopeEast, slopeNorthAndEastInner, slopeNorth, slopeWest,
50 | // outer slopes
51 | slopeSouth, slopeNorthAndEastOuter, slopeWest, slopeSouth,
52 | // diagonal slopes upper
53 | slopeSoutheastFull, flat, flat, slopeSouthwestFull,
54 | // diagonal slopes lower
55 | flat, slopeNortheastFull, flat, flat,
56 | // diagonal slopes full
57 | slopeSoutheastFull, slopeNortheastFull, slopeNorthwestFull, slopeSouthwestFull
58 | };
59 |
60 | const stuff::type_e trueCenterLeftTable[KIRBY_GEOM_TYPES] = {
61 | // nothing, flat, slopes up, slopes down
62 | nothing, flat, slopeSouthAndWestOuter, slopeEast,
63 | // cardinal slopes
64 | slopeSouth, slopeEast, slopeNorth, slopeWest,
65 | // inner slopes (SE, NE, NW, SW)
66 | slopeEast, slopeEast, slopeNorth, slopeSouth,
67 | // outer slopes
68 | slopeSouth, slopeNorthAndEastOuter, slopeWest, slopeSouthAndWestOuter,
69 | // diagonal slopes upper
70 | flat, flat, nothing, slopeSouthwestFull,
71 | // diagonal slopes lower
72 | slopeSoutheastFull, slopeNortheastFull, flat, flat,
73 | // diagonal slopes full
74 | slopeSoutheastFull, slopeNortheastFull, slopeNorthwestFull, slopeSouthwestFull
75 | };
76 |
77 | const stuff::type_e trueCenterRightTable[KIRBY_GEOM_TYPES] = {
78 | // nothing, flat, slopes up, slopes down
79 | nothing, flat, slopeNorthAndEastOuter, slopeSouth,
80 | // cardinal slopes
81 | slopeSouth, slopeEast, slopeNorth, slopeWest,
82 | // inner slopes (SE, NE, NW, SW)
83 | slopeSouth, slopeEast, slopeWest, slopeSouth,
84 | // outer slopes
85 | slopeEast, slopeNorthAndEastOuter, slopeNorth, slopeSouthAndWestOuter,
86 | // diagonal slopes upper
87 | flat, slopeNortheastFull, nothing, flat,
88 | // diagonal slopes lower
89 | slopeSoutheastFull, flat, flat, slopeSouthwestFull,
90 | // diagonal slopes full
91 | slopeSoutheastFull, slopeNortheastFull, slopeNorthwestFull, slopeSouthwestFull
92 | };
93 |
94 | metatile_t buildMetatile(int center, int left, int right,
95 | bool north, bool east, bool south, bool west,
96 | bool northStart, bool westStart) {
97 | metatile_t result = {nothing, nothing, {{0}}};
98 | int trueCenterLeft, trueCenterRight, trueLeft, trueRight;
99 |
100 | // do initial type mapping here
101 | if (left >= nothing)
102 | trueLeft = trueLeftTable[left];
103 | else trueLeft = left;
104 |
105 | if (right >= nothing)
106 | trueRight = trueRightTable[right];
107 | else trueRight = right;
108 |
109 | trueCenterLeft = trueCenterLeftTable[center];
110 | trueCenterRight = trueCenterRightTable[center];
111 |
112 | // get center tile
113 | metatile_t centerTile = findMetatile(metatilesTerrain, center, nothing);
114 |
115 | // get left edge
116 | metatile_t leftTile = findMetatile(metatilesTerrain, trueCenterLeft, trueLeft);
117 | // get right edge
118 | metatile_t rightTile = findMetatile(metatilesTerrain, trueCenterRight, trueRight);
119 |
120 | //get borders
121 | //also use center type mapping to maybe get bumpers for 2-way slopes etc
122 | metatile_t northBorder, southBorder, eastBorder, westBorder;
123 |
124 | if (center == slopeNorth && west && south) {
125 | westBorder = findMetatile(bordersAll, trueCenterLeft, trueRight);
126 | southBorder = westBorder;
127 | } else {
128 | if (westStart)
129 | westBorder = findMetatile(bordersWestStart, trueCenterLeft, trueRight);
130 | else
131 | westBorder = findMetatile(bordersWest, trueCenterLeft, trueRight);
132 |
133 | //southBorder = findMetatile(bordersSouth, trueCenterLeft, trueLeft);
134 | southBorder = findMetatile(bordersSouth, trueRightTable[center], trueLeft);
135 | }
136 |
137 | if (center == slopeWest && north && east) {
138 | northBorder = findMetatile(bordersAll, trueCenterRight, trueLeft);
139 | eastBorder = northBorder;
140 | } else {
141 | if (northStart)
142 | northBorder = findMetatile(bordersNorthStart, trueCenterRight, trueLeft);
143 | else
144 | northBorder = findMetatile(bordersNorth, trueCenterRight, trueLeft);
145 |
146 | //eastBorder = findMetatile(bordersEast, trueCenterRight, trueRight);
147 | eastBorder = findMetatile(bordersEast, trueLeftTable[center], trueRight);
148 | }
149 |
150 | // if the center is a lower part of a diagonal slope, put the edge tiles lower
151 | // (including bumpers)
152 | int yOffLeft = 0;
153 | int yOffRight = 0;
154 | int yOffBumper = 0;
155 | if (center >= slopesLower && center < endSlopesLower) {
156 | if (left != nothing)
157 | yOffLeft = 2;
158 | if (right != nothing)
159 | yOffRight = 2;
160 |
161 | yOffBumper = 2;
162 | }
163 |
164 | // combine tiles
165 | for (int y = 0; y < 8; y++)
166 | for (int x = 0; x < 8; x++) {
167 | if (south && y >= yOffBumper && TILE(southBorder.tiles[y - yOffBumper][x]))
168 | result.tiles[y][x] = southBorder.tiles[y - yOffBumper][x];
169 | else if (east && y >= yOffBumper && TILE(eastBorder.tiles[y - yOffBumper][x]))
170 | result.tiles[y][x] = eastBorder.tiles[y - yOffBumper][x];
171 | else if ((north || northStart) && y >= yOffBumper && TILE(northBorder.tiles[y - yOffBumper][x]))
172 | result.tiles[y][x] = northBorder.tiles[y - yOffBumper][x];
173 | else if ((west || westStart) && y >= yOffBumper && TILE(westBorder.tiles[y - yOffBumper][x]))
174 | result.tiles[y][x] = westBorder.tiles[y - yOffBumper][x];
175 | else if (y >= yOffLeft && x < 4 && left != nothing && TILE(leftTile.tiles[y - yOffLeft][x]))
176 | result.tiles[y][x] = leftTile.tiles[y - yOffLeft][x];
177 | else if (y >= yOffRight && x >= 4 && right != nothing && TILE(rightTile.tiles[y - yOffRight][x]))
178 | result.tiles[y][x] = rightTile.tiles[y - yOffRight][x];
179 | else result.tiles[y][x] = centerTile.tiles[y][x];
180 | }
181 |
182 | return result;
183 | }
184 |
185 | metatile_t buildObstacle(int center, int left, int right) {
186 | metatile_t result = {nothing, nothing, {{0}}};
187 | int trueCenter, trueLeft, trueRight;
188 |
189 | // do initial type mapping here
190 | if (center >= warpSouth2
191 | && center <= warpWest2) trueCenter = center - 4;
192 | else if (center == warpRed2) trueCenter = warpRed;
193 | else if (center == switchShine) trueCenter = switchShineBright;
194 | else if (center == switchRotateOn) trueCenter = switchRotate;
195 | else if (center == switchWaterOff) trueCenter = switchWater;
196 | else if (center >= startLine
197 | && center <= kirbyStartLine) trueCenter = startLine;
198 | else if ((center >= rotate) & (center < endRotate)) {
199 | if (center & 1) trueCenter = rotateCCW;
200 | else trueCenter = rotateCW;
201 | }
202 | else if ((center >= endRotate) & (center < endRotateOpposite)) {
203 | if (center & 1) trueCenter = rotateCCWOpposite;
204 | else trueCenter = rotateCWOpposite;
205 | }
206 | else trueCenter = center;
207 |
208 | // get center tile
209 | metatile_t centerTile = findMetatile(metatilesObstacles, trueCenter, nothing);
210 |
211 | // do pre-left type mapping here
212 | if (left == waterSouthAndEastOuter) trueLeft = waterEast;
213 | else if (left == waterNorthAndEastOuter) trueLeft = waterEast;
214 | else if (left >= startLine
215 | && left <= kirbyStartLine) trueLeft = startLine;
216 | else if ((left >= rotate) & (left < endRotate)) {
217 | if (left & 1) trueLeft = rotateCCW;
218 | else trueLeft = rotateCW;
219 | }
220 | else if ((left >= endRotate) & (left < endRotateOpposite)) {
221 | if (left & 1) trueLeft = rotateCCWOpposite;
222 | else trueLeft = rotateCWOpposite;
223 | }
224 | else trueLeft = left;
225 |
226 | // get left edge
227 | metatile_t leftTile = findMetatile(metatilesObstacles, trueCenter, trueLeft);
228 |
229 | // do pre-right type mapping here
230 | if (right == waterSouthAndEastOuter) trueRight = waterSouth;
231 | else if (right == waterSouthAndWestOuter) trueRight = waterSouth;
232 | else if (right >= startLine
233 | && right <= kirbyStartLine) trueRight = startLine;
234 | else if ((right >= rotate) & (right < endRotate)) {
235 | if (right & 1) trueRight = rotateCCW;
236 | else trueRight = rotateCW;
237 | }
238 | else if ((right >= endRotate) & (right < endRotateOpposite)) {
239 | if (right & 1) trueRight = rotateCCWOpposite;
240 | else trueRight = rotateCWOpposite;
241 | }
242 | else trueRight = right;
243 |
244 | // get right edge
245 | metatile_t rightTile = findMetatile(metatilesObstacles, trueCenter, trueRight);
246 |
247 | // combine tiles
248 | for (int y = 0; y < 8; y++)
249 | for (int x = 0; x < 4; x++) {
250 | if (trueLeft != nothing && TILE(leftTile.tiles[y][x]) > 0)
251 | result.tiles[y][x] = leftTile.tiles[y][x];
252 | else result.tiles[y][x] = centerTile.tiles[y][x];
253 |
254 | if (trueRight != nothing && TILE(rightTile.tiles[y][x + 4]) > 0)
255 | result.tiles[y][x + 4] = rightTile.tiles[y][x + 4];
256 | else result.tiles[y][x + 4] = centerTile.tiles[y][x + 4];
257 | }
258 |
259 | return result;
260 | }
261 |
262 | metatile_t findMetatile(const metatile_t *array, int type, int other) {
263 | metatile_t result = {nothing, nothing, {{0}}};
264 |
265 | for (int i = 0; array[i].type; i++) {
266 | if (array[i].type == type) {
267 | // use (in order):
268 | // selected tile touching nothing
269 | // selected tile touching selected other type
270 | if (array[i].adjacent == 0 || array[i].adjacent == other)
271 | result = array[i];
272 | if (array[i].adjacent == other)
273 | break;
274 | }
275 | }
276 | return result;
277 | }
278 |
279 | const uint16_t bottomTile[2][8] =
280 | {{ 16|PAL(7) , 17|PAL(7) , 1|PAL(7) , 205|PAL(7) , 202|PAL(7)|FB, 2|PAL(7) , 18|PAL(7) , 19|PAL(7) },
281 | { 0|PAL(0) , 0|PAL(0) , 16|PAL(7) , 17|PAL(7) , 18|PAL(7) , 19|PAL(7) , 0|PAL(0) , 0|PAL(0) }};
282 |
283 | const uint16_t stackTile[2][8] =
284 | {{214|PAL(7) , 203|PAL(7)|FB, 205|PAL(7)|FB, 1|PAL(7) , 2|PAL(7) , 202|PAL(7) , 90|PAL(7) , 215|PAL(7) },
285 | {180|PAL(7) , 205|PAL(7) , 203|PAL(7) , 203|PAL(7)|FB, 90|PAL(7) , 90|PAL(7)|FB, 202|PAL(7)|FB, 181|PAL(7) }};
286 |
--------------------------------------------------------------------------------
/src/metatile.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef METATILE_H
7 | #define METATILE_H
8 |
9 | #include "kirby.h"
10 |
11 | namespace stuff {
12 | enum type_e {
13 | nothing = 0,
14 | wall = -1,
15 |
16 | // terrain tile types
17 | flat = 0x1,
18 | slopesUp = 0x2,
19 | slopesDown = 0x3,
20 | slopes = 0x4,
21 | slopeSouth = 0x4,
22 | slopeEast = 0x5,
23 | slopeNorth = 0x6,
24 | slopeWest = 0x7,
25 | slopesDouble = 0x8,
26 | slopeSouthAndEastInner = 0x8,
27 | slopeNorthAndEastInner = 0x9,
28 | slopeNorthAndWestInner = 0xA,
29 | slopeSouthAndWestInner = 0xB,
30 | slopeSouthAndEastOuter = 0xC,
31 | slopeNorthAndEastOuter = 0xD,
32 | slopeNorthAndWestOuter = 0xE,
33 | slopeSouthAndWestOuter = 0xF,
34 | endSlopes = 0x10,
35 | slopesUpper = 0x10,
36 | slopeSoutheastUpper = 0x10,
37 | slopeNortheastUpper = 0x11,
38 | slopeNorthwestUpper = 0x12,
39 | slopeSouthwestUpper = 0x13,
40 | endSlopesUpper = 0x14,
41 | slopesLower = 0x14,
42 | slopeSoutheastLower = 0x14,
43 | slopeNortheastLower = 0x15,
44 | slopeNorthwestLower = 0x16,
45 | slopeSouthwestLower = 0x17,
46 | endSlopesLower = 0x18,
47 | slopesFull = 0x18,
48 | slopeSoutheastFull = 0x18,
49 | slopeNortheastFull = 0x19,
50 | slopeNorthwestFull = 0x1A,
51 | slopeSouthwestFull = 0x1B,
52 |
53 | // obstacle tile types
54 | extraTiles = 0x100,
55 |
56 | sand = 0x4,
57 | spikes = 0x5,
58 | spikesEx = spikes | extraTiles,
59 |
60 | current = 0x10,
61 | currentSouth = 0x10,
62 | currentEast = 0x11,
63 | currentNorth = 0x12,
64 | currentWest = 0x13,
65 | endCurrent = 0x14,
66 |
67 | arrowSouth = 0x14,
68 | arrowEast = 0x15,
69 | arrowNorth = 0x16,
70 | arrowWest = 0x17,
71 |
72 | boosterSouth = 0x18,
73 | boosterEast = 0x19,
74 | boosterNorth = 0x1A,
75 | boosterWest = 0x1B,
76 |
77 | ventNorthSouth = 0x1C,
78 | ventEastWest = 0x1D,
79 |
80 | bounce = 0x20,
81 | bounceSouth = 0x20,
82 | bounceEast = 0x21,
83 | bounceNorth = 0x22,
84 | bounceNorthEx = bounceNorth | extraTiles,
85 | bounceWest = 0x23,
86 | bounceWestEx = bounceWest | extraTiles,
87 | bounceFlat = 0x24,
88 |
89 | bumperNorthSouth = 0x28,
90 | bumperEastWest = 0x29,
91 | bumperSouthWest = 0x2A,
92 | bumperNorthWest = 0x2B,
93 | bumperNorthEast = 0x2C,
94 | bumperSouthEast = 0x2D,
95 |
96 | belts = 0x30,
97 | beltSouth = 0x30,
98 | beltEast = 0x31,
99 | beltNorth = 0x32,
100 | beltWest = 0x33,
101 | beltSlopes = 0x34,
102 | beltNorthUp = 0x34,
103 | beltSouthDown = 0x35,
104 | beltWestUp = 0x36,
105 | beltEastDown = 0x37,
106 | beltSouthUp = 0x38,
107 | beltNorthDown = 0x39,
108 | beltEastUp = 0x3A,
109 | beltWestDown = 0x3B,
110 |
111 | switchShineBright = 0x58,
112 | switchBright = 0x58,
113 | switchShine = 0x59,
114 | switchRotate = 0x5A,
115 | switchRotateOff = 0x5A,
116 | switchRotateOn = 0x5B,
117 | switchWater = 0x5C,
118 | switchWaterOn = 0x5C,
119 | switchWaterOff = 0x5D,
120 |
121 | water = 0x61,
122 | waterSouth = 0x64,
123 | waterEast = 0x65,
124 | waterNorth = 0x66,
125 | waterWest = 0x67,
126 | waterSouthAndEastInner = 0x68,
127 | waterNorthAndEastInner = 0x69,
128 | waterNorthAndWestInner = 0x6A,
129 | waterSouthAndWestInner = 0x6B,
130 | waterSouthAndEastOuter = 0x6C,
131 | waterNorthAndEastOuter = 0x6D,
132 | waterNorthAndWestOuter = 0x6E,
133 | waterSouthAndWestOuter = 0x6F,
134 | endWater = 0x70,
135 |
136 |
137 | rotate = 0x70,
138 | rotateCW = 0x70,
139 | rotateCCW = 0x71,
140 | endRotate = 0x78,
141 | rotateCWOpposite = 0x78,
142 | rotateCCWOpposite = 0x79,
143 | endRotateOpposite = 0x7C,
144 |
145 | warpSouth = 0xB0,
146 | warpEast = 0xB1,
147 | warpNorth = 0xB2,
148 | warpWest = 0xB3,
149 |
150 | // remapped to B0-B3 at render time
151 | warpSouth2 = 0xB4,
152 | warpEast2 = 0xB5,
153 | warpNorth2 = 0xB6,
154 | warpWest2 = 0xB7,
155 |
156 | warpRed = 0xB8,
157 | // remapped to B8
158 | warpRed2 = 0xB9,
159 |
160 | // all remapped to startLine
161 | startLineWest = 0xC0,
162 | startLine = 0xC1,
163 | startLineEast = 0xC2,
164 | kirbyStartLine = 0xC3
165 |
166 | };
167 | }
168 |
169 | typedef struct {
170 | stuff::type_e type, adjacent;
171 | uint16_t tiles[8][8];
172 | } metatile_t;
173 |
174 | extern const uint16_t stackTile[2][8];
175 | extern const uint16_t bottomTile[2][8];
176 | extern const metatile_t metatilesTerrain[];
177 | extern const metatile_t metatilesObstacles[];
178 |
179 | extern const stuff::type_e trueCenterLeftTable[];
180 | extern const stuff::type_e trueCenterRightTable[];
181 |
182 | extern const metatile_t bordersSouth[];
183 | extern const metatile_t bordersEast[];
184 | extern const metatile_t bordersNorth[];
185 | extern const metatile_t bordersWest[];
186 | extern const metatile_t bordersNorthStart[];
187 | extern const metatile_t bordersWestStart[];
188 | extern const metatile_t bordersAll[];
189 |
190 | metatile_t buildMetatile(int, int, int,
191 | bool, bool, bool, bool,
192 | bool northStart, bool westStart);
193 | metatile_t buildObstacle(int, int, int);
194 | metatile_t findMetatile(const metatile_t *array, int type, int other);
195 |
196 | #endif // METATILE_H
197 |
--------------------------------------------------------------------------------
/src/previewscene.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | previewscene.cpp
3 |
4 | Contains a QGraphicsScene subclass for rendering levels' isometric views. This is used by the preview
5 | window to display the "real" view of the level being edited. In the future this may be used to add a
6 | mini preview to the tile edit window or something.
7 |
8 | For the code which actually generates the isometric tile maps, see level.cpp.
9 |
10 | This code is released under the terms of the MIT license.
11 | See COPYING.txt for details.
12 | */
13 |
14 | #include
15 | #include
16 |
17 | #include "previewscene.h"
18 | #include "graphics.h"
19 | #include "level.h"
20 | #include "mapscene.h"
21 | #include "metatile.h"
22 |
23 | PreviewScene::PreviewScene(QObject *parent, leveldata_t *currentLevel)
24 | : QGraphicsScene(parent),
25 | level(currentLevel),
26 | sprites(true)
27 | {
28 | // set up sprite pixmaps
29 | dedede.load (":images/dedede.png");
30 | enemies.load (":images/enemies.png");
31 | gordo.load (":images/gordo3d.png");
32 | player.load (":images/kirby.png");
33 | }
34 |
35 | void PreviewScene::refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_WIDTH]) {
36 |
37 | // load the 3d tile resource
38 | // (NOTE: this is a temporary measure until actual graphic/palette
39 | // loading is implemented)
40 | // each palette zone is 216px tall and each row of tiles is
41 | // 32 tiles long.
42 | if (waterLevel(level))
43 | tiles.load(":images/3dtiles-water.png");
44 | else
45 | tiles.load(":images/3dtiles.png");
46 |
47 | int mapHeight = levelHeight(level);
48 | int mapWidth = level->header.width;
49 | int mapLength = level->header.length;
50 |
51 | int width = qMin(MAX_FIELD_WIDTH, (int)level->header.fieldWidth);
52 | int height = qMin(MAX_FIELD_HEIGHT, (int)level->header.fieldHeight);
53 |
54 | // reset the scene (remove all members)
55 | this->clear();
56 |
57 | // set the pixmap and scene size based on the playfield's size
58 | QPixmap pixmap(width * ISO_TILE_SIZE, height * ISO_TILE_SIZE);
59 | pixmap.fill(QColor(0, 0, 0, 0));
60 |
61 | this->setSceneRect(0, 0, width * ISO_TILE_SIZE, height * ISO_TILE_SIZE);
62 |
63 | // no level area = don't render anything
64 | if (mapLength + mapWidth == 0) {
65 | this->addPixmap(pixmap);
66 | this->update();
67 | return;
68 | }
69 |
70 | // used to render one tile w/ flip etc. for each layer
71 | QPixmap layer1tile(ISO_TILE_SIZE, ISO_TILE_SIZE);
72 | QPixmap layer2tile(ISO_TILE_SIZE, ISO_TILE_SIZE);
73 |
74 | QPainter layer1painter, layer2painter;
75 | QPainter painter;
76 |
77 | painter.begin(&pixmap);
78 |
79 | for (int h = 0; h < height; h++) {
80 | for (int w = 0; w < width; w++) {
81 | layer1tile.fill(QColor(0,0,0,0));
82 | layer2tile.fill(QColor(0,0,0,0));
83 |
84 | uint16_t tile1 = playfield[0][h][w];
85 | if (TILE(tile1)) {
86 | int y1 = (TILE(tile1) / 32) * ISO_TILE_SIZE
87 | + (PALN(tile1) * 216);
88 | int x1 = (TILE(tile1) % 32) * ISO_TILE_SIZE;
89 |
90 | layer1painter.begin(&layer1tile);
91 | layer1painter.drawPixmap(0, 0,
92 | tiles, x1, y1, ISO_TILE_SIZE, ISO_TILE_SIZE);
93 | layer1painter.end();
94 |
95 | // flip the pixmap horizontally and/or vertically
96 | layer1tile = QPixmap::fromImage(layer1tile.toImage().mirrored(tile1 & FH, tile1 & FV));
97 | }
98 |
99 | uint16_t tile2 = playfield[1][h][w];
100 | if (TILE(tile2)) {
101 | int y2 = (TILE(tile2) / 32) * ISO_TILE_SIZE
102 | + (PALN(tile2) * 216);
103 | int x2 = (TILE(tile2) % 32) * ISO_TILE_SIZE;
104 |
105 | layer2painter.begin(&layer2tile);
106 | layer2painter.drawPixmap(0, 0,
107 | tiles, x2, y2, ISO_TILE_SIZE, ISO_TILE_SIZE);
108 | layer2painter.end();
109 |
110 | // flip the pixmap horizontally and/or vertically
111 | layer2tile = QPixmap::fromImage(layer2tile.toImage().mirrored(tile2 & FH, tile2 & FV));
112 | }
113 |
114 | // draw the tile pixmaps onto the scene
115 | // if layer 1 has priority, draw layer 2 first
116 | if (tile1 & PRI) {
117 | painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer2tile);
118 | painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer1tile);
119 | } else {
120 | painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer1tile);
121 | painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer2tile);
122 | }
123 | }
124 | }
125 | // next, if sprites are enabled, draw them
126 | // most of this is copied from the 2D draw code
127 | if (sprites) {
128 | for (int y = 0; y < mapLength; y++) {
129 | for (int x = 0; x < mapWidth; x++) {
130 | QPixmap gfx;
131 | int frame;
132 | int obs = level->tiles[y][x].obstacle;
133 | int z = level->tiles[y][x].height;
134 |
135 | if (obs == 0) continue;
136 |
137 | // whispy woods (index 0x00 in enemies.png)
138 | if (obs == 0x02) {
139 | gfx = enemies;
140 | frame = 0;
141 |
142 | // kirby's start pos (kirby.png)
143 | // (this time also use the final boss version)
144 | } else if (obs == 0x0c || obs == 0xc3) {
145 | gfx = player;
146 | frame = 0;
147 |
148 | // dedede (frame 0 in dedede.png)
149 | } else if (obs == 0x0d) {
150 | gfx = dedede;
151 | frame = 0;
152 |
153 | // most enemies (ind. 01 to 13 in enemies.png)
154 | } else if (obs >= 0x40 && obs <= 0x52) {
155 | gfx = enemies;
156 | frame = obs - 0x40 + 1;
157 |
158 | // transformer (ind. 14 in enemies.png
159 | } else if (obs == 0x57) {
160 | gfx = enemies;
161 | frame = 0x14;
162 |
163 | // gordo (ind. 00 to 21 in gordo.png)
164 | } else if (obs >= 0x80 && obs <= 0x97) {
165 | gfx = gordo;
166 | frame = obs - 0x80;
167 |
168 | // kracko (index 15-17 in enemies.png)
169 | } else if (obs >= 0xac && obs <= 0xae) {
170 | gfx = enemies;
171 | frame = obs - 0xac + 0x15;
172 |
173 | // anything else - question mark (or don't draw)
174 | } else {
175 | obs = 0;
176 | }
177 |
178 | // draw the selected obstacle
179 | if (obs) {
180 | // horizontal: start at 0 pixels
181 | // move TILE_SIZE / 2 right for each positive move on the x-axis (west to east)
182 | // and TILE_SIZE / 2 left for each positive move on the y-axis (north to south)
183 | int startX = (TILE_SIZE / 2) * (x + (mapLength - y - 1));
184 | // start at h * TILE_SIZE / 4 tiles
185 | // move TILE_SIZE / 4 down for each positive move on the x-axis (west to east)
186 | // and TILE_SIZE / 4 down for each positive move on the y-axis (north to south)
187 | // and TILE_SIZE / 4 up for each positive move on the z-axis (tile z)
188 | // and then adjust for height of sprites
189 | int startY = (TILE_SIZE / 4) * (mapHeight + x + y - z + 4) - gfx.height();
190 | // move down half a tile's worth if the sprite is on a slope
191 | if (level->tiles[y][x].geometry >= stuff::slopes)
192 | startY += TILE_SIZE / 8;
193 |
194 | painter.drawPixmap(startX, startY,
195 | gfx, frame * TILE_SIZE, 0,
196 | TILE_SIZE, gfx.height());
197 | }
198 | }
199 | }
200 | }
201 |
202 | // finally, add the 3d map pixmap onto the scene
203 | painter.end();
204 |
205 | this->addPixmap(pixmap);
206 | this->update();
207 | }
208 |
209 |
--------------------------------------------------------------------------------
/src/previewscene.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef PREVIEWSCENE_H
7 | #define PREVIEWSCENE_H
8 |
9 | #include
10 | #include
11 | #include "level.h"
12 |
13 | class PreviewScene : public QGraphicsScene {
14 | Q_OBJECT
15 |
16 | private:
17 | leveldata_t *level;
18 | bool sprites;
19 |
20 | QPixmap tiles, dedede, enemies, gordo, player;
21 |
22 | public:
23 | PreviewScene(QObject *parent, leveldata_t *currentLevel);
24 | void refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_WIDTH]);
25 | };
26 |
27 | #endif // PREVIEWSCENE_H
28 |
--------------------------------------------------------------------------------
/src/previewwindow.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include "previewwindow.h"
7 | #include "ui_previewwindow.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "previewscene.h"
15 |
16 | PreviewWindow::PreviewWindow(QWidget *parent, leveldata_t *currentLevel) :
17 | QDialog(parent, Qt::Tool
18 | | Qt::CustomizeWindowHint
19 | | Qt::WindowTitleHint
20 | | Qt::WindowMinimizeButtonHint
21 | | Qt::WindowCloseButtonHint
22 | // | Qt::WindowStaysOnTopHint
23 | ),
24 | ui(new Ui::PreviewWindow),
25 | level(currentLevel),
26 | scene(new PreviewScene(this, currentLevel)),
27 | center(true)
28 | {
29 | ui->setupUi(this);
30 |
31 | //remove margins around graphics view
32 | this->layout()->setContentsMargins(0,0,0,0);
33 |
34 | ui->graphicsView->setScene(scene);
35 | }
36 |
37 |
38 | PreviewWindow::~PreviewWindow()
39 | {
40 | delete ui;
41 | }
42 |
43 | void PreviewWindow::setLevel(leveldata_t *level) {
44 | this->level = level;
45 | refresh();
46 | }
47 |
48 | void PreviewWindow::refresh() {
49 | // rebuild the 3D tile map
50 | makeIsometricMap(this->playfield, this->level);
51 | // and draw it
52 | scene->refresh(this->playfield);
53 | // refresh the display
54 | ui->graphicsView->update();
55 | }
56 |
57 | /*
58 | Converts 2D map coordinates into 3D display coordinates
59 | in order to center the preview display on a specific tile.
60 | */
61 | void PreviewWindow::centerOn(int x, int y) {
62 | if (!center) return;
63 |
64 | int z = level->tiles[y][x].height;
65 | int h = levelHeight(level);
66 | int l = level->header.length;
67 |
68 | // start at 32 pixels
69 | // move 32 right for each positive move on the x-axis (west to east)
70 | // and 32 left for each positive move on the y-axis (north to south)
71 | int centerX = 32 * (x + l - y);
72 | // start at 16 *(h + 1) pixels
73 | // move 16 down for each positive move on the x-axis (west to east)
74 | // and 16 down for each positive move on the y-axis (north to south)
75 | // and 16 up for each positive move on the z-axis (tile z)
76 | int centerY = 16 * (h + x + y - z + 1);
77 |
78 | ui->graphicsView->centerOn(centerX, centerY);
79 | }
80 |
81 | void PreviewWindow::enableCenter(bool center) {
82 | this->center = center;
83 | }
84 |
85 | void PreviewWindow::savePreview() {
86 | QList items = scene->items();
87 |
88 | if (!items.size()) {
89 | QMessageBox::information(NULL, tr("Save Level to Image"),
90 | tr("No level is currently open."),
91 | QMessageBox::Ok);
92 | return;
93 | }
94 |
95 | QSettings settings("settings.ini");
96 |
97 | QString imageName = QFileDialog::getSaveFileName(this,
98 | tr("Save Level to Image"),
99 | settings.value("PreviewWindow/fileName", "").toString(),
100 | tr("PNG image (*.png)"));
101 | if (!imageName.isNull()) {
102 | if (!((QGraphicsPixmapItem*)items[0])->pixmap().save(imageName)) {
103 | QMessageBox::warning(NULL, tr("Save Level to Image"),
104 | tr("Error saving %1.").arg(imageName),
105 | QMessageBox::Ok);
106 | }
107 |
108 | settings.setValue("PreviewWindow/fileName", imageName);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/previewwindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef PREVIEWWINDOW_H
7 | #define PREVIEWWINDOW_H
8 |
9 | #include
10 | #include "level.h"
11 | #include "previewscene.h"
12 |
13 | namespace Ui {
14 | class PreviewWindow;
15 | }
16 |
17 | class PreviewWindow : public QDialog
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | explicit PreviewWindow(QWidget *parent = 0, leveldata_t *currentLevel = 0);
23 | ~PreviewWindow();
24 |
25 | void setLevel(leveldata_t *level);
26 |
27 | public slots:
28 | void refresh();
29 | void centerOn(int x, int y);
30 | void enableCenter(bool);
31 | void savePreview();
32 |
33 | private:
34 | Ui::PreviewWindow *ui;
35 |
36 | leveldata_t *level;
37 |
38 | PreviewScene *scene;
39 | bool center;
40 |
41 | // The maximum area of the playfield is 13312 tiles.
42 | // The max for each dimension here was chosen as 384, but the
43 | // length * width cannot ever exceed 13312 tiles (or 26624 bytes.)
44 | // There are two playfields (one per layer) with the same size and layout.
45 | uint16_t playfield[2][MAX_FIELD_HEIGHT][MAX_FIELD_WIDTH];
46 |
47 | };
48 |
49 | #endif // PREVIEWWINDOW_H
50 |
--------------------------------------------------------------------------------
/src/previewwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | PreviewWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 300
10 | 300
11 |
12 |
13 |
14 |
15 | 200
16 | 200
17 |
18 |
19 |
20 | Preview
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 | 0
29 | 64
30 | 192
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/propertieswindow.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include
7 | #include
8 | #include "propertieswindow.h"
9 | #include "ui_propertieswindow.h"
10 | #include "kirby.h"
11 | #include "level.h"
12 |
13 | PropertiesWindow::PropertiesWindow(QWidget *parent) :
14 | QDialog(parent, Qt::CustomizeWindowHint
15 | | Qt::WindowTitleHint
16 | | Qt::WindowCloseButtonHint
17 | | Qt::MSWindowsFixedSizeDialogHint
18 | ),
19 | ui(new Ui::PropertiesWindow)
20 | {
21 | ui->setupUi(this);
22 |
23 | // prevent window resizing
24 | this->layout()->setSizeConstraint(QLayout::SetFixedSize);
25 |
26 | // add BG, palette, and music names to boxes
27 | for (int i = 0; i < NUM_BACKGROUNDS; i++)
28 | ui->comboBox_Background->addItem(bgNames[i].name);
29 |
30 | for (int i = 0; i < NUM_FG_PALETTES; i++) {
31 | ui->comboBox_Palette->addItem(paletteNames[i]);
32 | ui->comboBox_Water->addItem(paletteNames[i]);
33 | }
34 | for (StringMap::const_iterator i = musicNames.begin(); i != musicNames.end(); i++) {
35 | ui->comboBox_Music->addItem(i->second, i->first);
36 | }
37 |
38 | // set up signals to handle width/length constraints
39 | QObject::connect(ui->spinBox_Length, SIGNAL(valueChanged(int)),
40 | this, SLOT(setMaxLevelWidth(int)));
41 | QObject::connect(ui->spinBox_Width, SIGNAL(valueChanged(int)),
42 | this, SLOT(setMaxLevelLength(int)));
43 | }
44 |
45 | PropertiesWindow::~PropertiesWindow()
46 | {
47 | delete ui;
48 | }
49 |
50 | void PropertiesWindow::setMaxLevelWidth(int length) {
51 | ui->spinBox_Width->setMaximum(std::min(MAX_2D_SIZE, MAX_2D_AREA / length));
52 | }
53 |
54 | void PropertiesWindow::setMaxLevelLength(int width) {
55 | ui->spinBox_Length->setMaximum(std::min(MAX_2D_SIZE, MAX_2D_AREA / width));
56 | }
57 |
58 | void PropertiesWindow::startEdit(leveldata_t *level,
59 | int *bg, int *pal, int *water) {
60 |
61 | // save pointers
62 | this->level = level;
63 | this->bg = bg;
64 | this->pal = pal;
65 | this->water = water;
66 |
67 | // set height and width values
68 | ui->spinBox_Length->setValue(level->header.length);
69 |
70 | ui->spinBox_Width ->setValue(level->header.width);
71 |
72 | // set BG and FG stuff
73 | ui->comboBox_Background->setCurrentIndex(*bg);
74 | ui->comboBox_Palette ->setCurrentIndex(*pal);
75 | ui->comboBox_Water ->setCurrentIndex(*water);
76 |
77 | // set music value
78 | ui->comboBox_Music->setCurrentIndex(std::distance(musicNames.begin(),
79 | musicNames.find(level->music)));
80 |
81 | this->exec();
82 | }
83 |
84 | void PropertiesWindow::accept() {
85 | // apply level size
86 | level->header.length = ui->spinBox_Length->value();
87 | level->header.width = ui->spinBox_Width ->value();
88 |
89 | // apply FG/BG settings
90 | *bg = ui->comboBox_Background->currentIndex();
91 | *pal = ui->comboBox_Palette->currentIndex();
92 | *water = ui->comboBox_Water->currentIndex();
93 |
94 | // apply music setting
95 | level->music = ui->comboBox_Music->itemData(ui->comboBox_Music->currentIndex()).toUInt();
96 |
97 | level->modified = true;
98 | level->modifiedRecently = true;
99 |
100 | QDialog::accept();
101 | }
102 |
--------------------------------------------------------------------------------
/src/propertieswindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef PROPERTIESWINDOW_H
7 | #define PROPERTIESWINDOW_H
8 |
9 | #include
10 |
11 | #include "level.h"
12 |
13 | namespace Ui {
14 | class PropertiesWindow;
15 | }
16 |
17 | class PropertiesWindow : public QDialog
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | explicit PropertiesWindow(QWidget *parent = 0);
23 | ~PropertiesWindow();
24 |
25 | void startEdit(leveldata_t *level, int *bg, int *pal, int *water);
26 |
27 | public slots:
28 | void setMaxLevelWidth(int);
29 | void setMaxLevelLength(int);
30 |
31 | private:
32 | Ui::PropertiesWindow *ui;
33 |
34 | leveldata_t *level;
35 | int *bg, *pal, *water;
36 |
37 | private slots:
38 | void accept();
39 | };
40 |
41 | #endif // PROPERTIESWINDOW_H
42 |
--------------------------------------------------------------------------------
/src/propertieswindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | PropertiesWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 374
10 | 298
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | Level and Course Properties
21 |
22 |
23 | true
24 |
25 |
26 | -
27 |
28 |
29 | Course Properties
30 |
31 |
32 |
33 | QFormLayout::AllNonFixedFieldsGrow
34 |
35 | -
36 |
37 |
38 | Course Background
39 |
40 |
41 |
42 | -
43 |
44 |
45 | -
46 |
47 |
48 | (note: course BG setting effects normal, extra, and 2P courses with the same number)
49 |
50 |
51 | true
52 |
53 |
54 |
55 | -
56 |
57 |
58 | Foreground Palette
59 |
60 |
61 |
62 | -
63 |
64 |
65 | -
66 |
67 |
68 | Water Palettes
69 |
70 |
71 |
72 | -
73 |
74 |
75 |
76 |
77 |
78 | -
79 |
80 |
81 | Level Properties
82 |
83 |
84 | -
85 |
86 |
87 | Width
88 |
89 |
90 |
91 | -
92 |
93 |
94 | 1
95 |
96 |
97 |
98 | -
99 |
100 |
101 | Length
102 |
103 |
104 |
105 | -
106 |
107 |
108 | 1
109 |
110 |
111 |
112 | -
113 |
114 |
115 | Music
116 |
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
125 | -
126 |
127 |
128 | Qt::Horizontal
129 |
130 |
131 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | buttonBox
141 | accepted()
142 | PropertiesWindow
143 | accept()
144 |
145 |
146 | 248
147 | 254
148 |
149 |
150 | 157
151 | 274
152 |
153 |
154 |
155 |
156 | buttonBox
157 | rejected()
158 | PropertiesWindow
159 | reject()
160 |
161 |
162 | 316
163 | 260
164 |
165 |
166 | 286
167 | 274
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/src/romfile.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | romfile.cpp
3 | Contains functions for loading and saving data to/from a ROM file.
4 |
5 | Automatically detects valid game ROMs and allows reading/writing by CPU addresses, automatically
6 | adjusting for a 512-byte copier header if necessary.
7 |
8 | This code is released under the terms of the MIT license.
9 | See COPYING.txt for details.
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include
18 |
19 | #include "romfile.h"
20 | #include "compress.h"
21 |
22 | ROMFile::ROMFile() : QFile(),
23 | header(false),
24 | game(kirby),
25 | version(kirby_jp)
26 | {}
27 |
28 | ROMFile::game_e ROMFile::getGame() {
29 | return game;
30 | }
31 |
32 | ROMFile::version_e ROMFile::getVersion() {
33 | return version;
34 | }
35 |
36 | /*
37 | Converts a file offset to a LoROM address.
38 | If header == true, 512 bytes will be subtracted from the offset.
39 |
40 | Returns the corresponding SNES LoROM address (mapped from bank 80+)
41 | if successful, otherwise returns -1.
42 | */
43 | uint ROMFile::toAddress(uint offset) {
44 | // outside of fast lorom range = invalid
45 | if (offset >= 0x800000) return -1;
46 | // within header area = invalid
47 | if (header && offset < 0x200) return -1;
48 |
49 | // adjust for header
50 | offset -= (header ? 0x200 : 0);
51 | // map bank number and 32kb range within bank
52 | int address = (offset & 0x7FFF) | 0x8000
53 | | (offset & 0x3F8000) << 1
54 | | 0x800000;
55 |
56 | return address;
57 | }
58 |
59 | /*
60 | Converts a LoROM address to a file offset.
61 | If header == true, 512 bytes will be added to the offset.
62 |
63 | Returns the corresponding file offset if successful, otherwise
64 | returns -1.
65 | */
66 | uint ROMFile::toOffset(uint address) {
67 | uint offset = ((address & 0x7FFF) | ((address & 0x7F0000) >> 1))
68 | + (header ? 0x200 : 0);
69 |
70 | return offset;
71 | }
72 |
73 | /*
74 | Opens the file and also verifies that it is one of the ROMs supported
75 | by the editor; displays a dialog and returns false on failure.
76 |
77 | For KDC, this checks for the string "ninten" at various offsets.
78 | It also determines whether the ROM is headered or not.
79 | */
80 | const struct {int address; char string[7]; uint8_t region; ROMFile::game_e game;} versions[] = {
81 | // Kirby Bowl (JP)
82 | {0x8ECE, "ninten", 0, ROMFile::kirby},
83 | // Kirby's Dream Course (US/EU)
84 | {0x8ECC, "ninten", 1, ROMFile::kirby},
85 | {0x8ECC, "ninten", 2, ROMFile::kirby},
86 |
87 | // Special Tee Shot (currently debug mode only)
88 | // checks title of rom (which can and may be changed); find something better
89 | {0xFFC0, "\xBD\xCD\xDF\xBC\xAC\xD9", 0, ROMFile::sts},
90 |
91 | {0, "", 0, ROMFile::kirby}
92 | };
93 |
94 | bool ROMFile::openROM(OpenMode flags) {
95 | if (!this->open(flags))
96 | return false;
97 |
98 | QSettings settings(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/settings.ini",
99 | QSettings::IniFormat, this);
100 | char buf[6];
101 | uint8_t region = readByte(0xFFD9);
102 | bool debug = settings.value("MainWindow/debug", false).toBool();
103 |
104 | header = this->size() % BANK_SIZE == 0x200;
105 |
106 | for (int i = 0; versions[i].address; i++) {
107 | #ifdef QT_NO_DEBUG
108 | if (!debug && versions[i].game == sts)
109 | continue;
110 | #endif
111 |
112 | readBytes(versions[i].address, 6, buf);
113 | if (!memcmp(buf, versions[i].string, 6)
114 | && (versions[i].game == sts || region == versions[i].region)) {
115 | game = versions[i].game;
116 | version = (ROMFile::version_e)i;
117 | return true;
118 | }
119 | }
120 |
121 | // no valid ROM detected - throw error
122 | QMessageBox::critical(NULL, "Open File",
123 | "Please select a valid Kirby Bowl or Kirby's Dream Course ROM.",
124 | QMessageBox::Ok);
125 | return false;
126 | }
127 |
128 | /*
129 | Reads data from a file into a pre-existing char buffer.
130 | If "size" == 0, the data is decompressed, with a maximum decompressed
131 | size of 65,536 bytes (64 kb).
132 |
133 | Returns the size of the data read from the file, or 0 if the read was
134 | unsuccessful.
135 | */
136 | size_t ROMFile::readBytes(uint addr, uint size, void *buffer) {
137 | if (!size) {
138 | char packed[DATA_SIZE];
139 | this->seek(toOffset(addr));
140 | if (this->read(packed, DATA_SIZE) > 0)
141 | return unpack((uint8_t*)packed, (uint8_t*)buffer);
142 | else
143 | return 0;
144 |
145 | } else {
146 | this->seek(toOffset(addr));
147 | return qMax((int)read((char*)buffer, size), 0);
148 | }
149 | }
150 |
151 | uint8_t ROMFile::readByte(uint addr) {
152 | uint8_t data;
153 | readBytes(addr, 1, &data);
154 | return data;
155 | }
156 |
157 | uint16_t ROMFile::readInt16(uint addr) {
158 | uint16_t data;
159 | readBytes(addr, 2, &data);
160 | return data;
161 | }
162 |
163 | uint32_t ROMFile::readInt32(uint addr) {
164 | uint32_t data;
165 | readBytes(addr, 2, &data);
166 | return data;
167 | }
168 |
169 | /*
170 | Reads a 24-bit ROM pointer from a file, then dereferences the pointer and
171 | reads from the address pointed to. If "size" == 0, the data is decompressed.
172 |
173 | Returns the size of data read, or 0 if unsuccessful.
174 | */
175 | size_t ROMFile::readFromPointer(uint addr, uint size, void *buffer) {
176 | uint pointer;
177 | // first, read the pointer
178 | this->seek(toOffset(addr));
179 | if (!(this->read((char*)&pointer, sizeof(uint)))) return 0;
180 | pointer &= 0x00FFFFFF;
181 |
182 | // then, read from where it points
183 | return readBytes(pointer, size, buffer);
184 | }
185 |
186 | /*
187 | Writes data to an ROM address in a file.
188 | Since this (currently) only deals with SNES ROMs, offsets will be moved up
189 | to 32kb boundaries when needed.
190 |
191 | Returns the next available address to write data to.
192 | */
193 | uint ROMFile::writeBytes(uint addr, uint size, void *buffer) {
194 | uint offset = toOffset(addr);
195 | uint spaceLeft = BANK_SIZE - (addr % BANK_SIZE);
196 |
197 | // move offset forward if there's not enough space left in the bank
198 | if (size > spaceLeft)
199 | offset += spaceLeft;
200 |
201 | // now write data to file
202 | this->seek(offset);
203 | this->write((const char*)buffer, size);
204 |
205 | // return new ROM address
206 | return toAddress(pos());
207 | }
208 |
209 | uint ROMFile::writeByte(uint addr, uint8_t data) {
210 | return writeBytes(addr, 1, &data);
211 | }
212 |
213 | uint ROMFile::writeInt16(uint addr, uint16_t data) {
214 | return writeBytes(addr, 2, &data);
215 | }
216 |
217 | uint ROMFile::writeInt32(uint addr, uint32_t data) {
218 | return writeBytes(addr, 4, &data);
219 | }
220 |
221 | /*
222 | Writes data to an offset in a file, and then writes the 24-bit SNES pointer
223 | to that data into a second offset.
224 |
225 | Returns the next available offset to write data to.
226 | */
227 | uint ROMFile::writeToPointer(uint pointer, uint addr,
228 | uint size, void *buffer) {
229 | // write the data
230 | addr = writeBytes(addr, size, buffer);
231 |
232 | // write the data pointer
233 | // (do this AFTER data is written in case writeData needs to move to the next
234 | // ROM bank)
235 | int startAddr = addr - size;
236 | this->seek(toOffset(pointer));
237 | this->write((const char*)&startAddr, 3);
238 |
239 | return addr;
240 | }
241 |
--------------------------------------------------------------------------------
/src/romfile.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef FILE_H
7 | #define FILE_H
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | #define BANK_SIZE 0x8000
14 |
15 |
16 | class ROMFile: public QFile {
17 | public:
18 |
19 | enum version_e {
20 | kirby_jp,
21 | kirby_us,
22 | kirby_eu,
23 | sts_jp
24 | };
25 |
26 | enum game_e {
27 | kirby,
28 | sts
29 | };
30 |
31 | ROMFile();
32 |
33 | bool openROM(OpenMode flags);
34 |
35 | ROMFile::game_e getGame();
36 | ROMFile::ROMFile::version_e getVersion();
37 |
38 | uint toAddress(uint offset);
39 | uint toOffset(uint addr);
40 |
41 | size_t readBytes(uint addr, uint size, void *buffer);
42 | uint8_t readByte(uint addr);
43 | uint16_t readInt16(uint addr);
44 | uint32_t readInt32(uint addr);
45 | size_t readFromPointer(uint addr, uint size, void *buffer);
46 | uint writeBytes(uint addr, uint size, void *buffer);
47 | uint writeByte(uint addr, uint8_t data);
48 | uint writeInt16(uint addr, uint16_t data);
49 | uint writeInt32(uint addr, uint32_t data);
50 | uint writeToPointer(uint ptr, uint addr, uint size, void *buffer);
51 |
52 | private:
53 | bool header;
54 | ROMFile::game_e game;
55 | ROMFile::version_e version;
56 | };
57 |
58 | #endif // FILE_H
59 |
--------------------------------------------------------------------------------
/src/tileeditwindow.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include
7 |
8 | #include "tileeditwindow.h"
9 | #include "ui_tileeditwindow.h"
10 |
11 | #include "kirby.h"
12 | #include "level.h"
13 | #include "metatile.h"
14 |
15 | using namespace stuff;
16 |
17 | uint getCheckBox(const QCheckBox *box);
18 | void setCheckBox(QCheckBox *box, int state);
19 |
20 | TileEditWindow::TileEditWindow(QWidget *parent) :
21 | QDialog(parent, Qt::CustomizeWindowHint
22 | | Qt::WindowTitleHint
23 | | Qt::WindowCloseButtonHint
24 | | Qt::MSWindowsFixedSizeDialogHint
25 | ),
26 | ui(new Ui::TileEditWindow)
27 | {
28 | ui->setupUi(this);
29 |
30 | // prevent window resizing
31 | this->layout()->setSizeConstraint(QLayout::SetFixedSize);
32 |
33 | // add geometry types to dropdown
34 | for (StringMap::const_iterator i = kirbyGeometry.begin(); i != kirbyGeometry.end(); i++) {
35 | if (i->second.size()) {
36 | ui->comboBox_Terrain->addItem(i->second, i->first);
37 | }
38 | }
39 |
40 | // add obstacle types to dropdown (only valid ones)
41 | for (StringMap::const_iterator i = kirbyObstacles.begin(); i != kirbyObstacles.end(); i++) {
42 | if (i->second.size()) {
43 | ui->comboBox_Obstacle->addItem(i->second, i->first);
44 | }
45 | }
46 | }
47 |
48 | TileEditWindow::~TileEditWindow()
49 | {
50 | delete ui;
51 | }
52 |
53 | int TileEditWindow::startEdit(leveldata_t *level, QRect sel) {
54 | this->level = level;
55 |
56 | // iterate through the tiles in the selection and build up the
57 | // selection info.
58 | selX = sel.x();
59 | selY = sel.y();
60 | selWidth = sel.width();
61 | selLength = sel.height();
62 | maptile_t tile = level->tiles[selY][selX];
63 |
64 | // set initial values to compare against
65 | // (to determine when the selection covers multiple values)
66 | tileInfo.geometry = tile.geometry;
67 | tileInfo.obstacle = tile.obstacle;
68 | tileInfo.height = tile.height;
69 | tileInfo.bumperEast = tile.flags.bumperEast;
70 | tileInfo.bumperNorth = tile.flags.bumperNorth;
71 | tileInfo.bumperSouth = tile.flags.bumperSouth;
72 | tileInfo.bumperWest = tile.flags.bumperWest;
73 |
74 | tileInfo.layer = tile.flags.layer;
75 |
76 | tileInfo.maxHeight = tile.height;
77 | tileInfo.minHeight = tile.height;
78 |
79 | for (int v = selY; v < selY + selLength; v++) {
80 | for (int h = selX; h < selX + selWidth; h++) {
81 | tile = level->tiles[v][h];
82 | if (tile.geometry != tileInfo.geometry)
83 | tileInfo.geometry = -1;
84 | if (tile.obstacle != tileInfo.obstacle)
85 | tileInfo.obstacle = -1;
86 | if (tile.height != tileInfo.height)
87 | tileInfo.height = -1;
88 |
89 | tileInfo.minHeight = std::min((int)tileInfo.minHeight, (int)tile.height);
90 | tileInfo.maxHeight = std::max((int)tileInfo.maxHeight, (int)tile.height);
91 |
92 | if (tile.flags.bumperEast != tileInfo.bumperEast)
93 | tileInfo.bumperEast = -1;
94 | if (tile.flags.bumperNorth != tileInfo.bumperNorth)
95 | tileInfo.bumperNorth = -1;
96 | if (tile.flags.bumperSouth != tileInfo.bumperSouth)
97 | tileInfo.bumperSouth = -1;
98 | if (tile.flags.bumperWest != tileInfo.bumperWest)
99 | tileInfo.bumperWest = -1;
100 |
101 | if (tile.flags.layer != tileInfo.layer)
102 | tileInfo.layer = -1;
103 | }
104 | }
105 |
106 | // multiple height values = raise/lower instead of set height
107 | bool relativeHeight = tileInfo.height == -1;
108 |
109 | // set the control values to the stuff in the tileinfo
110 | if (tileInfo.geometry == -1) {
111 | ui->comboBox_Terrain->insertItem(0, tr("(multiple)"), 0);
112 | ui->comboBox_Terrain->setCurrentIndex(0);
113 | } else {
114 | ui->comboBox_Terrain->setCurrentIndex(std::distance(kirbyGeometry.begin(),
115 | kirbyGeometry.find(tileInfo.geometry)));
116 | }
117 | if (tileInfo.obstacle == -1) {
118 | ui->comboBox_Obstacle->insertItem(0, tr("(multiple)"));
119 | ui->comboBox_Obstacle->setCurrentIndex(0);
120 | } else {
121 | ui->comboBox_Obstacle->setCurrentIndex(std::distance(kirbyObstacles.begin(),
122 | kirbyObstacles.find(tileInfo.obstacle)));
123 | }
124 |
125 | // set bumper checkboxes to correct values
126 | setCheckBox(ui->checkBox_North, tileInfo.bumperNorth);
127 | setCheckBox(ui->checkBox_South, tileInfo.bumperSouth);
128 | setCheckBox(ui->checkBox_East, tileInfo.bumperEast);
129 | setCheckBox(ui->checkBox_West, tileInfo.bumperWest);
130 |
131 | // set up the height slider
132 | relativeHeight = tileInfo.height == -1;
133 | int min, max, value;
134 | if (relativeHeight) {
135 | ui->label_Height->setText(tr("Raise/Lower"));
136 | min = 0 - tileInfo.minHeight;
137 | max = MAX_HEIGHT - tileInfo.maxHeight;
138 | value = 0;
139 | } else {
140 | ui->label_Height->setText(tr("Height"));
141 | min = 0;
142 | max = MAX_HEIGHT;
143 | value = tileInfo.height;
144 | }
145 | ui->horizontalSlider_Height->setMinimum(min);
146 | ui->spinBox_Height->setMinimum(min);
147 | ui->horizontalSlider_Height->setMaximum(max);
148 | ui->spinBox_Height->setMaximum(max);
149 | ui->horizontalSlider_Height->setValue(value);
150 |
151 | // set up the layer radio buttons
152 | ui->radioButton_Keep->setChecked(tileInfo.layer == -1);
153 | ui->radioButton_Layer1->setChecked(tileInfo.layer == 0);
154 | ui->radioButton_Layer2->setChecked(tileInfo.layer == 1);
155 |
156 | // run the modal dialog
157 | return this->exec();
158 | }
159 |
160 | /*
161 | This array maps conveyor belt types to their counterparts for the different slope types.
162 | Dimension 1 is the belt direction, dimension 2 is the slope direction
163 | */
164 | const stuff::type_e conveyorMap[4][4] = {
165 | //slope south east north west
166 | //beltSouth
167 | {beltSouthDown, nothing, beltSouthUp, nothing},
168 | //beltEast
169 | {nothing, beltEastDown, nothing, beltEastUp},
170 | //beltNorth
171 | {beltNorthUp, nothing, beltNorthDown, nothing},
172 | //beltWest
173 | {nothing, beltWestUp, nothing, beltWestDown}
174 | };
175 |
176 | void TileEditWindow::accept() {
177 | // get terrain and obstacle values
178 | // (if multiple geom/obstacle values are selected, only use index >0)
179 | if (tileInfo.geometry != -1 || ui->comboBox_Terrain->currentIndex() > 0)
180 | tileInfo.geometry = ui->comboBox_Terrain->itemData(ui->comboBox_Terrain->currentIndex()).toUInt();
181 |
182 | if (tileInfo.obstacle != -1 || ui->comboBox_Obstacle->currentIndex() > 0)
183 | //tileInfo.obstacle = stringVal(ui->comboBox_Obstacle->currentText());
184 | tileInfo.obstacle = ui->comboBox_Obstacle->itemData(ui->comboBox_Obstacle->currentIndex()).toUInt();
185 |
186 | // get bumper checkbox status
187 | tileInfo.bumperNorth = getCheckBox(ui->checkBox_North);
188 | tileInfo.bumperSouth = getCheckBox(ui->checkBox_South);
189 | tileInfo.bumperEast = getCheckBox(ui->checkBox_East);
190 | tileInfo.bumperWest = getCheckBox(ui->checkBox_West);
191 |
192 | // get height slider status
193 | int newHeight = ui->horizontalSlider_Height->value();
194 | bool relativeHeight = tileInfo.height == -1;
195 |
196 | // get layer selection status
197 | bool layer1 = ui->radioButton_Layer1->isChecked();
198 | bool layer2 = ui->radioButton_Layer2->isChecked();
199 |
200 | // after the tile edit window is done, apply the changes to the tiles
201 | for (int v = selY; v < selY + selLength; v++) {
202 | for (int h = selX; h < selX + selWidth; h++) {
203 | maptile_t *newTile = &(level->tiles[v][h]);
204 |
205 | if (newTile->geometry == 0 && tileInfo.geometry == -1) {
206 | continue;
207 | } else if (tileInfo.geometry == 0) {
208 | *newTile = noTile;
209 | continue;
210 | }
211 |
212 | if (tileInfo.geometry >= 0)
213 | newTile->geometry = tileInfo.geometry;
214 | if (tileInfo.obstacle >= 0) {
215 | // handle multiple types for water hazard based on terrain value
216 | if (tileInfo.obstacle == water
217 | && newTile->geometry >= slopes && newTile->geometry < endSlopes)
218 | newTile->obstacle = water - 1 + newTile->geometry;
219 | // handle multiple types for bounce pads
220 | else if (tileInfo.obstacle == bounceFlat
221 | && newTile->geometry >= slopes && newTile->geometry < slopesDouble)
222 | newTile->obstacle = bounce + newTile->geometry - slopes;
223 | // handle multiple types for conveyor belts
224 | else if (tileInfo.obstacle >= belts && tileInfo.obstacle < beltSlopes
225 | && newTile->geometry >= slopes && newTile->geometry < slopesDouble)
226 | newTile->obstacle = conveyorMap[tileInfo.obstacle - belts][newTile->geometry - slopes];
227 | else
228 | newTile->obstacle = tileInfo.obstacle;
229 | }
230 |
231 | if (tileInfo.bumperNorth >= 0)
232 | newTile->flags.bumperNorth = tileInfo.bumperNorth;
233 | if (tileInfo.bumperSouth >= 0)
234 | newTile->flags.bumperSouth = tileInfo.bumperSouth;
235 | if (tileInfo.bumperEast >= 0)
236 | newTile->flags.bumperEast = tileInfo.bumperEast;
237 | if (tileInfo.bumperWest>= 0)
238 | newTile->flags.bumperWest = tileInfo.bumperWest;
239 |
240 | if (relativeHeight)
241 | newTile->height += newHeight;
242 | else
243 | newTile->height = newHeight;
244 |
245 | if (layer2) newTile->flags.layer = 1;
246 | else if (layer1) newTile->flags.layer = 0;
247 |
248 | newTile->flags.dummy = 0;
249 | }
250 | }
251 |
252 | QDialog::accept();
253 | }
254 |
255 | /*
256 | Set a checkbox's tristate value and check state based on the
257 | appropriate value in tileinfo.
258 | */
259 | void setCheckBox(QCheckBox *box, int state) {
260 | box->setTristate(state == -1);
261 | switch (state) {
262 | case -1:
263 | box->setCheckState(Qt::PartiallyChecked);
264 | break;
265 | case 1:
266 | box->setCheckState(Qt::Checked);
267 | break;
268 | default:
269 | box->setCheckState(Qt::Unchecked);
270 | }
271 | }
272 |
273 | /*
274 | Get a checkbox's value for tileinfo based on its check state
275 | */
276 | uint getCheckBox(const QCheckBox *box) {
277 | return (box->checkState() == Qt::PartiallyChecked)
278 | ? -1 : box->checkState() / 2;
279 | }
280 |
--------------------------------------------------------------------------------
/src/tileeditwindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef TILEEDITWINDOW_H
7 | #define TILEEDITWINDOW_H
8 |
9 | #include
10 |
11 | #include "level.h"
12 |
13 | namespace Ui {
14 | class TileEditWindow;
15 | }
16 |
17 | class TileEditWindow : public QDialog
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | explicit TileEditWindow(QWidget *parent = 0);
23 | ~TileEditWindow();
24 |
25 | int startEdit(leveldata_t *level, QRect sel);
26 |
27 | private:
28 | Ui::TileEditWindow *ui;
29 |
30 | leveldata_t *level;
31 | tileinfo_t tileInfo;
32 | int selX, selY, selLength, selWidth;
33 | bool relativeHeight;
34 |
35 | private slots:
36 | void accept();
37 | };
38 |
39 | #endif // TILEEDITWINDOW_H
40 |
--------------------------------------------------------------------------------
/src/tileeditwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | TileEditWindow
4 |
5 |
6 | Qt::ApplicationModal
7 |
8 |
9 |
10 | 0
11 | 0
12 | 400
13 | 273
14 |
15 |
16 |
17 |
18 | 0
19 | 0
20 |
21 |
22 |
23 |
24 | 400
25 | 250
26 |
27 |
28 |
29 |
30 | 16777215
31 | 16777215
32 |
33 |
34 |
35 | Edit Tiles
36 |
37 |
38 |
39 | :/images/main16.png :/images/main16.png
40 |
41 |
42 | true
43 |
44 |
45 | -
46 |
47 |
48 | QLayout::SetDefaultConstraint
49 |
50 |
51 | 0
52 |
53 | -
54 |
55 |
56 | Terrain
57 |
58 |
59 | false
60 |
61 |
62 | -
63 |
64 |
65 | 31
66 |
67 |
68 | Qt::Horizontal
69 |
70 |
71 |
72 | -
73 |
74 |
75 | false
76 |
77 |
78 |
79 | -
80 |
81 |
82 | Height
83 |
84 |
85 |
86 | -
87 |
88 |
89 | 31
90 |
91 |
92 |
93 | -
94 |
95 |
96 | Qt::Horizontal
97 |
98 |
99 | QSizePolicy::Fixed
100 |
101 |
102 |
103 | 40
104 | 20
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | -
113 |
114 |
115 | Obstacles
116 |
117 |
118 | -
119 |
120 |
121 | Qt::Horizontal
122 |
123 |
124 |
125 | 40
126 | 20
127 |
128 |
129 |
130 |
131 | -
132 |
133 |
134 | false
135 |
136 |
137 |
138 | -
139 |
140 |
141 |
142 | 44
143 | 16777215
144 |
145 |
146 |
147 | Bumpers
148 |
149 |
150 |
151 | -
152 |
153 |
154 | North
155 |
156 |
157 |
158 | -
159 |
160 |
161 | South
162 |
163 |
164 |
165 | -
166 |
167 |
168 | East
169 |
170 |
171 |
172 | -
173 |
174 |
175 | West
176 |
177 |
178 |
179 |
180 |
181 |
182 | -
183 |
184 |
185 | Rendering
186 |
187 |
188 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
189 |
190 |
191 | -
192 |
193 |
194 | true
195 |
196 |
197 | Layer 1
198 |
199 |
200 | true
201 |
202 |
203 |
204 | -
205 |
206 |
207 | true
208 |
209 |
210 |
211 | 0
212 | 0
213 |
214 |
215 |
216 | Qt::LeftToRight
217 |
218 |
219 | Layer 2
220 |
221 |
222 | true
223 |
224 |
225 |
226 | -
227 |
228 |
229 | Qt::Horizontal
230 |
231 |
232 |
233 | 40
234 | 20
235 |
236 |
237 |
238 |
239 | -
240 |
241 |
242 | Qt::Horizontal
243 |
244 |
245 |
246 | 40
247 | 20
248 |
249 |
250 |
251 |
252 | -
253 |
254 |
255 | true
256 |
257 |
258 | Keep
259 |
260 |
261 | false
262 |
263 |
264 |
265 |
266 |
267 |
268 | -
269 |
270 | -
271 |
272 |
273 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | spinBox_Height
289 | valueChanged(int)
290 | horizontalSlider_Height
291 | setValue(int)
292 |
293 |
294 | 123
295 | 68
296 |
297 |
298 | 265
299 | 68
300 |
301 |
302 |
303 |
304 | horizontalSlider_Height
305 | valueChanged(int)
306 | spinBox_Height
307 | setValue(int)
308 |
309 |
310 | 265
311 | 68
312 |
313 |
314 | 123
315 | 68
316 |
317 |
318 |
319 |
320 | buttonBox
321 | accepted()
322 | TileEditWindow
323 | accept()
324 |
325 |
326 | 199
327 | 250
328 |
329 |
330 | 199
331 | 136
332 |
333 |
334 |
335 |
336 | buttonBox
337 | rejected()
338 | TileEditWindow
339 | reject()
340 |
341 |
342 | 199
343 | 250
344 |
345 |
346 | 199
347 | 136
348 |
349 |
350 |
351 |
352 |
353 |
--------------------------------------------------------------------------------
/src/version.h:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #ifndef VERSION_H
7 | #define VERSION_H
8 |
9 | #define INFO_TITLE "Kirby's Dream Course Editor"
10 | #define INFO_MYNAME "Devin Acker (Revenant)\0"
11 | #define INFO_LEGAL "Copyright 2013-2015 by Revenant\0"
12 |
13 | #define INFO_DESC "Kirby's Dream Course editor\0"
14 | #define INFO_VERS "1.13c\0"
15 | #define INFO_VNUM 1, 13, 2, 0
16 | #define INFO_NAME "KDCEditor\0"
17 | #define INFO_FILE "KDCEditor.exe\0"
18 |
19 | #endif // VERSION_H
20 |
--------------------------------------------------------------------------------
/src/windows.rc:
--------------------------------------------------------------------------------
1 | /*
2 | This code is released under the terms of the MIT license.
3 | See COPYING.txt for details.
4 | */
5 |
6 | #include
7 | #include "version.h"
8 |
9 | 0 ICON "images/main.ico"
10 |
11 | VS_VERSION_INFO VERSIONINFO
12 | FILEVERSION INFO_VNUM
13 | PRODUCTVERSION INFO_VNUM
14 | FILEFLAGSMASK 0x3fL
15 | FILEFLAGS 0
16 | FILEOS VOS_NT_WINDOWS32
17 | FILETYPE VFT_APP
18 | FILESUBTYPE VFT2_UNKNOWN
19 | {
20 | BLOCK "VarFileInfo"
21 | {
22 | VALUE "Translation", 0x409, 1200
23 | }
24 | BLOCK "StringFileInfo"
25 | {
26 | BLOCK "040904b0"
27 | {
28 | VALUE "CompanyName", INFO_MYNAME
29 | VALUE "FileDescription", INFO_DESC
30 | VALUE "FileVersion", INFO_VERS
31 | VALUE "InternalName", INFO_NAME
32 | VALUE "LegalCopyright", INFO_LEGAL
33 | VALUE "OriginalFilename", INFO_FILE
34 | VALUE "ProductName", INFO_DESC
35 | VALUE "ProductVersion", INFO_VERS
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------