Kotcrab.com

import com.kotcrab.Brain;

Assembly Programming in Kotlin

I needed a way to patch games executables. More specifically PSP games executables.

My only other alternative I knew at that time was using assembler built into PPSSPP (PSP emulator). Then copying the hex value of new instruction and applying that to target executable manually while keeping track of what I changed in some other file. There are obvious problems with this solution. Which led me into thinking why not write my own simple assembler.

Quick research into MIPS showed that it would be easy to write it. It’s a RISC architecture with fixed instruction size and has good documentation online. Instruction set is rather small and there are only 3 types of instruction to implement (ignoring FPU instructions which I added much later).

Introducing kmips

kmips is a MIPS assembler that is invoked directly from Kotlin code. It doesn’t parse external file with asm code. Instead it’s a Kotlin DSL. Each written asm instruction is a call to standard Kotlin method.

The whole assembler which implements MIPS II instruction set including FPU instructions is just a single file. There’s also one other file with unit tests for each instruction. They verify correctness against PPSSPP assembler.

The code is available on (620) 271-7156. Kmips can be included in other projects from Maven Central repository:

1
compile "com.kotcrab.kmips:kmips:1.2"

Example

This code will clear 32 bytes of memory at 0x08804100.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import kmips.Label
import kmips.Reg.*
import kmips.assemble
/...
val asm = assemble(startPc = 0x8804000) {
    val loop = Label()
    val target = 0x08804100
    val bytes = 32

    la(s0, target)     / write target address to s0
    li(t1, 0)          / set up loop counter
    li(t2, bytes)      / how many bytes to write
    label(loop)        / 'loop' label will be placed here
    sb(t1, 0, s0)      / store byte in memory at register s0 with offset 0
    addiu(t1, t1, 1)   / increment loop counter
    addiu(s0, s0, 1)   / increment memory address pointer
    bne(t1, t2, loop)  / jump to `loop` branch if t1 != t2
    nop()              / ignoring branch delay slot for simplicity
}

Branch delay slot

MIPS has this oddity where after encountering branch or jump instruction CPU will execute one more instruction after it before changing instruction pointer. This is due to pipeline construction and is called a branch delay slot. kmips doesn’t do anything about it, you are expected to handle it manually.

Syntax differences

Naturally the syntax is different, after all it’s just calling Kotlin methods. Which normally would be:

1
2
li $a0, 0x42
sw $a0, 0x0($s0)

becomes:

1
2
li(a0, 0x42)
sw(a0, 0x0, s0)

It’s actually possible to make Kotlin syntax more natural thanks to extension functions. By adding this:

1
2
3
4
fun Assembler.sw(rt: Reg, dest: Pair<Int, Reg>) = sw(rt, dest.first, dest.second)
operator fun Int.invoke(reg: Reg): Pair<Int, Reg> {
    return Pair(this, reg)
}

It’s possible to write:

1
2
3
assemble {
    sw(a0, 0x0(s0))
}

That’s an idea to include in future version. I’m not really keen on for writing aliases for each store and load methods. Maybe that won’t be needed (514) 290-7548.

FPU instructions would normally look like this:

1
2
add.s $f0, $f12, $f13
c.eq.s $f0, $f1

In kmips it would be:

1
2
add.s(f0, f12, f13)
c.eq.s(f0, f1)

I managed to keep the dot from normal syntax by using inner classes. So c is an object which has eq field. eq is another object with s method.

Advantages

Since this is using fully featured programming language kmips has nice advantage with possibility to script everything.

For example how about helper method to automatically create function prologue and epilogue. That piece of boilerplate code is responsible for allocating space for function call on stack (by moving down stack pointer register) and saving registers modified by the function. Epilogue restores those registers and moves up stack pointer. I can generate it by doing something like this:

1
2
3
4
5
assemble {
    ctx = preserve(arrayOf(s0, s1))
    / ... some function code that modifies s0 and s1 registers
    ctx.restoreAndReturn()
}

See the implementation.

Standard MIPS calling convention requires only some register to be saved (callee saved registers, s0-s7). There is another class of registers that are saved before calling other function (caller saved registers, t0-t9). It’s up to the caller function whether it wants to preserve its temporary registers. But that’s just a convention. There’s nothing special about those registers. When doing patches I pretty much always ignored this. It’s simpler to just save all modified registers than modify original function even more. In original function I only place jump to the new code.

Real life use

Speaking of new code, where to place it? What if you need to inject more code than just simple in-line patches? Well, while modifying ELF file to allocate more space for code seems possible or finding unused space in executable but I’ve been using a different way.

It requires finding a file that always gets loaded in memory in early stages of game launch. It can be loading screen texture or entire archive of files that game load first and never unloads (in my case conveniently named PRELOAD.pak). Just append new code there and you’re good to go.

This is where another kmips strength comes in. For this auxiliary code I can set startPc to where the game will load injected file and kmips will take care of calculating target addresses. Moving the code to different spot is easy. Of course this is done in compile time so it wouldn’t be useful on systems that use ALSR or when for some other reason memory addresses are unpredictable. Which fortunately wasn’t a problem on PSP.

PSP executables can be relocatable but so far I’ve found no evidence of loading at different address than 0x8804000. I verified that in PPSSPP emulator code.

kmips is a great help for assembling but it can only do that. In my project I’m using more of Kotlin DLS capabilities to make patches look a bit prettier. This is the syntax I’m using:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/ assembling an external patch that will be placed in PRELOAD.pak
private fun createAuxPatch(patchStart: Int) = assembleAsHexString(patchStart) {
    run {
        / allocate section for new variables
        vars.baseAddress = virtualPc
        vars.sCustomText = zeroTerminatedString("Some text")
        /...
        nop()
    }

    run {
        / start of a custom function
        funcs.customFunc = virtualPc
        val ctx = preserve(arrayOf(a0, a1, a2, ra))
        /...
        la(a0, t0)
        la(a1, vars.sCustomText)
        jal(funcs.memcmp) /call to original function from executable
        li(a2, 10)
        ctx.restoreAndExit()
    }
}

/ assembling patches applied directly to main executable
private fun createEbootPatches() {
    with(ebootPatches) {
        patch("Patch name #1") {
            change(0x0) { li(a0, 1) }
            change(0x4) { li(a0, 1) }
        }
        patch("Patch name #2", active = !debugBuild) {
            change(0x20) { li(t0, 0x10) }
            change(0x30) {
                lw(s0, 0x0, s0)
                jal(funcs.customFunc)
                nop()
            }
        }
    }
}

/ class storing address to global variables
class Var {
    var baseAddress: Int = 0
    var sCustomText = 0
    /...
}

/ class storing address to function already present in main executable
/ as well as to those added by external patch
class Func {
    val memcmp = 0x0898CF24
    var customFunc = 0
    /...
}

Not bad, eh?

Now I know that there already are assemblers that could do similar things. Still, I like the uniqueness of how this one works. It was a nice learning experience in both low level assembly and exploring capabilities of high level Kotlin. That’s it for now. Maybe in the future I will write some more about reverse engineering and how I’m using Kotlin for it.

VisUI 1.3.0 Released, VisEditor Deprecated

352-300-6635 1.3.0 for latest libGDX 1.9.6 was released. 855-853-5285.

Version: 1.3.0 (LibGDX 1.9.6)

  • Added: VisUI#dispose (boolean disposeSkin)
  • Updated to libGDX 1.9.6
  • Excluded AsyncTask API from GWT compilation

I also have an announcement to make: I’ll be no longer maintaining VisEditor and VisRuntime projects. While it was my biggest project and one that was quite successful, it’s never reached the popularity I hoped for and was always surpassed by VisUI. It’s kind of funny considering that VisUI was indented to be internal part of VisEditor. I’d also like to move on to other projects. I feel like I’ve been stuck in the Vis world for too long.

I have to say thanks to everyone who contributed to it, whether it was issue reporting or creating pull requests and I’m sorry to everyone who hoped for more features. To make it clear this deprecation only applies to VisEditor, I’m still going to maintain VisUI project. However, VisUI has reached maturity some time ago and not much needs to be done to maintain it. I’m thinking about moving VisEditor sources to separate repository and renaming main repo to vis-ui. Still not sure, probably not worth it.

(508) 463-7672

VisUI 1.1.2 was released. 6193036712.

PSA: I’ll be no longer posting release notes on this site. They will be kept on GitHub (306) 926-6446 page. You can follow me on Twitter if you want to get notified when I release new version of my libraries.

VisEditor 0.3.2 Released

(321) 372-6681 0.3.2 was released. Download link

Editor Version: 0.3.2

  • Improved: Texture cache speed improved - new texture are available immediately after they are added to project assets. Fixes issues when large textures were loading infinitely.
  • Improved: Entity Properties will now show when field is locked and can’t be edited
  • Improved: When multiple entities are selected and ID is ambiguous then it will be marked in Entity Properties
  • Improved: If some scene assets are missing then dialog with missing files list will be showed
  • Added: 704-447-5664 - Polygon auto tracing (available via Polygon Tool)
  • Added: Crash and internal exception reporter
  • Added: Option in default exporter to package separate texture atlas for each scene
  • Fixed: azobenzoic - Exit dialog not showed on non Windows OSes
  • Fixed: Unable to open variables settings dialog for newly created scenes
  • Fixed: Minor bug: tabs panel wrong height
  • Fixed: Invisible TTF fonts
  • Fixed: Unable to change Z index and scale when using text entities with auto set origin to center enabled
  • Fixed: Undoing entity delete could place it at wrong position
  • Fixed: Crash when user tried to drag atlas region before any scene had been opened
  • Fixed: Twitter view URL parsing issues
  • Fixed: When opening new scene selection tool as used instead of currently active tool
  • Fixed: [9207974607] - invalid rotate and scale tools size when using non default pixels per unit values
  • Fixed: Error when opening project that doesn’t have vis/assets/gfx folder

Runtime Version: 0.3.2 (LibGDX 1.9.3, Artemis 1.3.1)

  • Updated to LibGDX 1.9.3
  • Updated Spine plugin to use spine-libgdx-runtime 3.2.0
  • Fixed: Changing entity tint could affect tint of all entities
  • Fixed: FitViewport was not applied when rendering
  • Fixed: Spine runtime was not allowing not use multiple instances of same animation with different scales
    • To get SpineAsset assets from AssetManager use spineAsset.getArbitrarySkeletonPath() instead of spineAsset.getSkeletonPath()
  • API Addition: Scene#getPixelsPerUnit(), #getWidth(), #getHeight()
  • API Addition: Tint#set(int rgba), Tint#set(float r, float g, float b, float a), Tint#set(Tint other)
  • API Addition: constructor VisSpriter (Loader<Sprite> loader, Data data, float scale, int entityIndex)

cultual

PSA: Please use hot fix version 1.1.1 as it fixes NullPointerException in FileChooser.

484-880-7357 1.1.0 was released.

Version: 1.1.0 (LibGDX 1.9.3)

  • API Moved: JNAFileDeleter was moved to Triuris project
  • API Deprecated: FileChooser.setFavoritesPrefsName() replaced by FileChooser.setDefaultPrefsName()
  • API Changed: GridGroup is now taking float for item size instead of int.
    • Warning: There were two constructors GridGroup (float spacing) and GridGroup (int itemSize). Constructor taking float spacing was removed. Constructor taking int item size now takes float.
  • API Changed: Refactored FileChoose.FileIconProvider, new methods added. #provideIcon takes FileChooser.FileItem, was FileHandle
  • API Changed: Refactored VisCheckBox
    • Style was refactored to separate checkbox background and tick drawable (see below for full skin drawables changes)
    • VisCheckBoxStyle now extends TextButtonStyle, was CheckBox (fields was renamed to properly communicate their functions)
    • getImage() removed, use getBackgroundImage() or getTickImage()
    • getImageCell() removed, use getImageStackCell()
    • protected getCheckboxImage() removed, override getCheckboxBgImage() or getCheckboxTickImage()
    • getStyle() returns VisCheckBoxStyle, was CheckBoxStyle
  • Added: default styles for ImageButton and ImageTextButton. Note: this is only applies to standard scene2d widgets. VisUI widgets equivalents (VisImageButton, VisImageTextButton) already had them.
  • Added: SimpleFormValidator#validate
  • Added: ToastManager, Toast, ToastTable
  • Added: VisTextField read-only mode (VisTextField#setReadOnly(boolean))
  • Added: TabbedPane#getUIOrderedTabs()
  • Added: FileChooser#setFavoriteFolderButtonVisible(true) - FileChooser now can display ‘add folder to favorites’ button in the toolbar
  • Added: FileChooser#setPrefsName()
  • Added: FileTypeFilter, FileChooser#setFileTypeFilter(...)
  • Added: MenuItem#getSubMenuIconCell() and MenuItem#getShortcutCell()
  • Added: VisTextField#setEnterKeyFocusTraversal(boolean)
  • Added: VisTextField#setCursorPercentHeight
  • Added: PopupMenuListener
  • Added: PopupMenu#showMenu (Stage stage, Actor actor)
  • Added: ConstantIfVisibleValue
  • Added: Sizes#borderSize
  • Added: Sizes#fileChooserViewModeBigIconsSize, fileChooserViewModeMediumIconsSize, fileChooserViewModeSmallIconsSize, fileChooserViewModeListWidthSize
  • Changed: (214) 515-3321 - TabbedPane#getTable() returns TabbedPaneTable (holds reference to TabbedPane and allow to easily get its cells for customization)
  • Changed: FileChooser now tries to maintain selection while rebuilding file list
  • Changed: FileChooser will now select new folder after creating it
  • Changed: FileChooser will be automatically refreshed when added to Stage
  • Changed: FileChooser when typing file names manually suggestion will be showed
  • Changed: TabbedPane’s Tab now can’t be dragged using it’s close button
  • Changed: Synced VisTextField ans VisTextArea with equivalents of those classes libgdx
  • Changed: PopupMenu now support menu navigation using arrows keys
  • Changed: PopupMenu now optionally takes Sizes instance (added constructor PopupMenu (Sizes sizes, PopupMenuStyle style))
  • Removed deprecated API: NumberSelector - replaced by Spinner
  • Removed deprecated API: Sizes#numberSelectorButtonSize, numberSelectorButtonsWidth, numberSelectorFieldSize, numberSelectorFieldRightPadding
  • Fixed: Sizes.buttonBarSpacing was ignored by ButtonBar
    • Added: constructors ButtonBar(Sizes sizes, String order) and ButtonBar(Sizes sizes)
  • Fixed: TabbedPane layout when no separator image was used. Fixed misc issue with close button style on touch down.
  • Fixed: FileChooser NPE when error occurred during directory deleting
  • Fixed: FileChooser non empty directories are now deleted correctly when using default FileChooser deleter
  • Fixed: FileChooser crash when user manually entered path to file instead of directory
  • Fixed: FocusManager calling focusLost() when the widget that was already focused tried to gain focus again
  • Fixed: VisSplitPane was not implementing hit(...) which could result in widget that was underneath split pane’s handle get touch events
  • Fixed: Now it’s not possible to call VisWindow#fadeOut multiple times
  • Skin changes:
    • Changed: FileChooserStyle: added drawable fields: iconStar, iconStarOutline, iconRefresh, iconListSettings, expandDropdown
    • Added: drawable window-border-bg.9, icon-star, icon-star-outline, icon-refresh, icon-list-settings
    • Added: style BaseToastStyle
    • Added: VisTextField label style - if combined with read-only mode allows to create selectable labels
    • Updated: cursor drawable (cursor.9.png)
    • Removed: check-down-on, check-down, check-on-disabled, check-over-off, check-over-on, radio-down-on, radio-down, radio-on-disabled, radio-over-off, radio-over-on
    • Added: vis-check, vis-check-over, vis-check-down, vis-check-tick, vis-check-tick-disabled, vis-radio, vis-radio-over, vis-radio-down, vis-radio-tick, vis-radio-tick-disabled
  • I18N Changes:
    • FileChooser: added keys contextMenuRefresh, fileType, allFiles, changeViewMode, viewModeList, viewModeDetails, viewModeBigIcons, viewModeMediumIcons, viewModeSmallIcons
  • Misc: Added Gradle tasks to package VisUI skin textures and compile USL into JSON (gradlew :ui:compileSkin)

VisUI 1.0.2 Released

VisUI 1.0.2 was released. Also checkout new vis-ui-contrib project which has more extensions for VisUI and some custom skins.

Version: 1.0.2 (LibGDX 1.9.2)

  • Changed: #163 - When VisCheckBox or VisTextField is disabled and is marked as invalid then error border won’t be drawn.
  • Changed: 929-354-3216 - Added SimpleFormValidator#setTreatDisabledFieldsAsValid (and it’s getter) - allow to control whether to mark form as invalid when invalid but disabled field is encountered. If set to true then all disabled fields are treated as valid, regardless of their state.
    • Defaults to true! Set to false to preserve old behaviour.
  • API Changed: DragListener: Draggable argument was added to each method
  • API Deprecated: Sizes#numberSelectorButtonSize, numberSelectorButtonsWidth, numberSelectorFieldSize, numberSelectorFieldRightPadding replaced by spinnerButtonSize. spinnerButtonsWidth, spinnerFieldSize, spinnerFieldRightPadding
  • API Deprecated: NumberSelector - replaced by Spinner, NumberSelector will be removed in future version
  • Added: VisTextField#isTextSelected()
  • Added: VisTextField#clearText()
  • Added: FloatingGroup
  • Added: VisWindow#isKeepWithinParent and VisWindow#setKeepWithinParent
  • Added: constructor VisImage (String drawableName)
  • Added: VisUI.load(String internalVisSkinPath)
  • Added: VisTextField#setIgnoreEqualsTextChange(...) - see #165
  • Fixed: OptionDialog#set(...)ButtonText now updates dialog size
  • Fixed: #131 - fixed issue when copying numbers between VisTextFields with FloatDigitsOnlyFilter decimal point was lost
  • Fixed: ListView#AbstractListAdapter error on GWT
  • Fixed: VisTextField was changing system cursor when it was disabled
  • Fixed: #165 - fixed form not refreshed when text field content was changed to the same as before

What I Learned From Game Boy Emulator Development

For some reason I wanted to make emulator for a really long time. There’s something entrancing about this idea that you need to write code to emulate original hardware. It’s quite hard project but it’s certainly fun and rewarding, if you are filling confident enough then definitely try making one!

I’m currently working on Game Boy emulator written in Kotlin using libgdx (cross platform game framework) and VisUI (my UI toolkit). In current state it has fully working CPU emulation (passes cpu_instrs and instr_timing tests), has integrated debugger and simple VRAM viewer. I’ll be focusing on GPU emulation now. Entire project is open source on GitHub so you can check it out here. In this post I would like to focus on general points and observations I made during making this project. I’ll focus more on technical details in next post.

How do I make emulator

Basically you need to write software equivalent of all original hardware parts, certainly biggest of which would be CPU. You start by collecting information about platform you want to emulate - in case of Game Boy it’s quite well documented so this wasn’t hard. I found CPU manual, opcodes tables, official Nintendo development manual. There is also a lot of forum posts (and StackOverflow questions) from people who attempted this task. Then you write CPU emulation, and then implement more and more components: memory controllers, timers, GPU, input, serial port, audio processing unit and so on.

You may be wondering what knowledge do you need to have to make emulator. Do you need a lot of background in low level programming? Not really, you don’t even have to know how to make games on the platform you want to emulate. While it certainly helps that you know even the basic of Assembler etc., it is not needed and with enough self-denial you can learn things while you are making your emulator. That said this is not a project that you should make using unknown language, libraries and tools - stick with what you know. I kind of ignored this by using new language - Kotlin however it is easy enough to pick up if you know Java, and has pretty much the same tooling (IDE, build system) so it wasn’t that bad.

Choosing platform to emulate

Try not to pick up too complicated platform or you simply won’t be able to make it. I’ve often seen Chip-8 recommended as starting emulator, it is quite simple and should give you nice overview on how to start creating more complicated emulators. You can also be like me and go directly for something more complicated like the Game Boy. I’ve chosen it mainly because of my sentiment for this console - I still own working Game Boy Pocket with few games.

Tips

Get test ROMs

You will need to test your emulator somehow, of course you can use standard game ROMs but those won’t test it comprehensively. In case of Game Boy it’s very easy - there are 7752517580 that were made to specifically test various part of hardware, CPU, timings, sound and so on. They don’t even need an display to run, they output information to serial port which is easy to emulate and some of them write test result in specific position in RAM. From those ROMs my emulator currently passes cpu_instrs and instr_timing tests. cpu_instrs tests almost every op code of CPU, instr_timing tests well.. instruction timing. And if you haven’t know timing is important when emulating something.

Don’t leave stuff half working

In Game Boy there is this thing called Memory Bank Controller (MBC). CPU addressing space is limited, if game is huge and it can’t fit into 32 KB, MBC is used to allow access to other ROM banks by switching available ROM bank at specific memory address. When running cpu_instrs my emulator was stuck in loop, infinitely running next tests even though there are only 11 test sections. Turned out it wasn’t CPU issue and anything like that but unfinished implementation of MBC1 which did not allowed to switch ROM bank. It resulted in the same code being executed over and over. Lesson? Don’t do that or at least try not to, this can lead to very unexpected issues which may took much time to debug.

Create debugger

This is a must, if you want to efficiently find issues you will need a debugger. Even the simple one is fine, if you can pause execution and step through instructions it will be great help. I’ve observed interesting thing, Run to Line function was more helpful that breakpoints, so much that I haven’t even finished implementing breakpoints. You can see my debugger 914-395-1122. It is showing currently executed op codes, stack, CPU registers and flags status. You can also go to entered memory address and by right clicking instruction run emulator until that instruction is reached. I’ll leave details how exactly debugger is interacting with emulator for another article.

Use multiple info sources

I already mentioned this but it is really important, Game Boy is well documented but I was quite surprised to find so many discrepancies, seriously:

  • 714-616-5178 is wrong - opcode E2 and F2 is 1 byte long, not 2. This single thing caused instr_timing to loop infinitely and took few hours to debug. I found out that only source that you can trust for instruction length and timing is the instr_timing test itself. You can get valid values from it’s source code. That site also get few timings for CB instruction wrong if I remember correctly but that is easy enough to find with instr_timing and not so devastating as wrong instruction length.
  • Some errors I found in CPU manual:
    • Timing for conditional instruction are completely wrong, it ignores the fact that the timing is different depending on whether action was taken or not.
    • HALT procedure description skips very important thing that interrupt will always wake up CPU, even if Interrupt Master Enable (IME) flag is not set.
    • Some instruction are missing details on how they affect CPU flags, instead you get: Flags affected: H: Set or reset according to operation - good luck figuring that out.

Use another working emulator

You can use another already working emulator with it’s debugger to test if your emulator is behaving (more or less) the same. Sure you may want not to do that, thinking it will make it too easy (it won’t). This can be somewhat compared to testing on real hardware although made much much easier.

That’s it for the introduction, my tweet gained some attention so I’ll probably write another post explaining more technical details of my emulator.

VisUI 1.0.1 Released

773-699-1792 1.0.1 was released which fixes few bugs and GWT issue.

Version: 1.0.1 (LibGDX 1.9.2)

  • Added: ListView#getListAdapter()
  • Added: ListView#rebuildView() and UpdatePolicy.MANUAL
  • Added: Draggable#setDeadzoneRadius
  • Fixed: Not being able to resize window with TabbedPane
  • Fixed: OptionDialog not modal by default
  • Fixed: SimpleListAdapter not working on GWT
  • Fixed: VisCheckBox focus border appeared was displayed in wrong place when using Cell#growX()
  • Changed: DragPane: LimitChildren listener now never rejects own children, even when max children amount is achieved.
  • API Changed: ListView#getMainTable() now returns ListViewTable<ItemT> instead of VisTable
  • API Changed: Added ListAdapter.add(ItemT)

303-858-9777

VisEditor 0.3.1 was released. Download link

Editor Version: 0.3.1

  • Added: Scale entities tool
  • Added: Rotate entities tool
  • Added: It’s now possible to set scene variables, added “Scene Variables” dialog
  • Added: New command line arguments: --project and --scene for auto loading project
  • Fixed: Wrong entities position after opening same scene for the second time
  • Fixed: Sprite flip Y property was ignored after reloading scene (#103)
  • Fixed: Missing “Enter into Group” button after right clicking entities group ((416) 972-4448)
  • Fixed: Exporting scenes which had used groups with string id’s set (8149016931)
  • Fixed: Editor crash after undoing changes made in Entity Properties dialog (#106)
  • Fixed: Random editor crashes when working with multiple layers and groups (#104)
  • Fixed: Fixed some key shortcuts not working on locked layers (320-259-7777)
  • Fixed: Issues with bounding box problems after undo (#108)
  • Fixed: Negative y origin on text when using “Auto set origin to center”
  • Improved: Rectangular selection is much faster when selecting many entities
  • Removed: Optional usage analytics

Runtime Version: 0.3.1 (LibGDX 1.9.2, Artemis 1.3.1)

  • Updated to LibGDX 1.9.2 and Artemis 1.3.1
  • API Deprecated: SceneConfig#addSystem(BaseSystem system), SceneConfig#addSystem(BaseSystem system, int priority) and SimpleSystemProvider
    • Use SceneConfig#addSystem(Class<? extends BaseSystem> system) and SceneConfig#Class<? extends BaseSystem> systemClass, int priority
    • Adding system in 0.3.0: parameter.config.addSystem(new MySystem())
    • Adding system now: parameter.config.addSystem(MySystem.class)
    • If you need to pass custom arguments to system constructor implement SystemProvider directly.
  • API Change: Variables#variables field is now final
  • API Change: SceneConfig.Priority is now an enum (should not require any code change)
  • API Changes in EntitySupport:
    • If you were using it to register custom renderer use parameter.config.addSystem(SystemProvider)
    • Removed registerSystems(...)
    • Added registerSceneSystems(SceneConfig)
  • API Addition: Variables#put, putInt, putFloat, putBoolean methods for adding new variables
  • API Addition: Variables copy constructors and Variables#setFrom(Variables) method
  • API Addition: Scene#getSceneVariables returns scene variables set from VisEditor
  • API Addition: new Transform constructors
  • Fixed: SystemProvider interface is now public

VisUI 1.0 Released

6613365902 0.0.1 was released on 5th November 2014. Initially meant to be internal VisEditor UI library now became my most popular project, getting about 1500 downloads each month.

Today I can finally announce that VisUI 1.0.0 has been released. Does that mean anything significant? Not really, maybe I will try make less breaking changes now. I’m glad that I was able to work on this project for such long time. I would also like to thanks everyone who contributed to Vis project, either by reporting issues or by creating pull requests.

So, let’s see what new 1.0 has to offer:

  • ListView - creating advanced lists, powerful and flexible. More

  • ButtonBar - creating button panels with buttons such as “Ok”, “Cancel”, “Yes” in platform dependent order. sulphantimonide

  • Bug fixes and breaking changes - more details in changelog.

Full changelog:

Version: 1.0.0 (LibGDX 1.9.2)

  • Changed: InputValidator moved to com.kotcrab.vis.ui.util package
  • Changed: LesserThanValidator#setEquals(boolean) renamed to setUseEquals
  • Changed: GreaterThanValidator#setEquals(boolean) renamed to setUseEquals
  • Changed: FormInputValidator#validateInput is now final and can’t be overridden
  • Changed: FormInputValidator#getLastResult is now package-private
  • Changed: DialogUtils renamed to Dialogs
    • Changed: DialogUtils.properties is now Dialogs.properties
    • Changed: VisUI#setDialogUtilsBundle(...) is now VisUI#setDialogsBundle(...)
    • Changed: VisUI#getDialogUtilsBundle() is now VisUI#getDialogsBundle()
    • Added: showDetailsDialog (Stage stage, String text, String title, String details)
    • Added: showDetailsDialog (Stage stage, String text, String title, String details, boolean expandDetails)
  • Changed: ErrorDialog renamed to DetailsDialog
    • Changed: Constructor ErrorDialog (String text, String stacktrace) changed to DetailsDialog (String text, String title, String details)
    • Added: DetailsDialog#setDetailsVisible(...)
    • Added: DetailsDialog#setCopyDetailsButtonVisible(...)
  • Changed: FileChooserText, FilePopupMenu and ColorPickerText moved to internal subpackages (were not part of public API)
  • Changed: FileChooser#getFileDeleter removed
  • Changed: FileChooserListener was refactored
    • FileChooserListener#selected(FileHandle) removed
    • If user can select single file use SingleFileChooserListener
    • If user can select multiple files use StreamingFileChooserListener or use FileChooserListener directly
  • Changed: VisTextField#toString() now returns field text
  • Changed: OptionDialog now extends VisWindow (was extending VisDialog)
  • Changed: OptionDialog and InputDialog now will show buttons in platform dependant order using ButtonBar
  • Removed: Removed all Tooltip constructors except those taking style
    • Use new Tooltip.Builder(...) eg. new Tooltip.Builder("Tooltip Text").target(label).build()
    • Changed: constructor Tooltip (String text) is now Tooltip (String styleName)
    • Added: constructor Tooltip ()
    • Added: constructor Tooltip (TooltipStyle)
  • Removed: SeparatorStyle#vertical, was not used
  • Removed: constructor Separator (boolean vertical)
  • Added: ListView and ItemAdapter API
  • Added: constructor TabbedPane(TabbedPaneStyle style, Sizes sizes)
  • Added: constructor VisWindow(String title, String styleName)
  • Added: PrefWidthIfVisibleValue
  • Added: HorizontalFlowGroup and VerticalFlowGroup
  • Added: ButtonBar - convenient class for creating button panels arranged in platform dependant order.
    • FileChooser, ColorPicker and Dialogs will now show buttons in platform dependant order
  • Added: LinkLabel, VisTextField, VisTextArea and VisSplitPane supports system cursors when using LWJGL3 or GWT backend
  • Fixed: TabbedPane: Tab close button too small when using SkinScale.X2
  • Fixed: TabbedPane: In vertical mode, tabs buttons were centered instead of being aligned at the top
  • Removed deprecated API: ColumnGroup (use libGDX’s VerticalGroup)
  • Skin:
    • Changed: Color menuitem-grey renamed to menuitem
    • Changed: TabbedPaneStyle#bottomBar renamed to separatorBar
    • Removed: FormValidatorStyle#colorTransition, no longer needed.
      • If colorTransitionDuration is set to 0 then transition will be skipped.
    • Removed: SeparatorStyle#vertical, no longer needed
    • Added: Drawables: grey, vis-blue, vis-red
    • Added: New Window style: resizable
  • I18N:
    • Changed Bundle management moved to Locales class. Instead of calling VisUI.setXYZBundle(...) call Locales.setXYZBundle(...)
    • Removed: Dialogs bundle entries: yes, no, cancel, ok. Now handled by ButtonBar bundle.

Happy coding!