- install gammu
- create a .gammurc:
# your phone BT address:
gammu --nokiaaddfile Application NameOfJarWithoutExtension
December 11, 2008
December 5, 2008
Getting that to work was no picnic, though.
Here's a basic overview of how it's supposed to work:
- choose a list of things you want from your GPS device and set it all up in a Criteria object
- instantiate a LocationProvider by calling LocationProvider.getInstance(myCriteriaObject);
- periodically ask for a new location by calling provider.getLocation(timeout)
- or implement a LocationListener interface, and register that with the provider. You can then specify some refresh intervals and timeouts and whatnot. Fortunately, you can also leave it on default values.
Well, yes, but with a caveat: there's no listProviders or equivalent method. If the phone has more than one provider, it can return pretty much anything it wants.
And it turns out that e.g. Nokia N95 has more than one provider, and one of them is very dumb and coincidentally that's the one that is selected when you specify "empty" criteria (the default Criteria instance means basically "anything will do")
It seems to work when you say that you want to get speed, course, altitude and you allow it to cost money:
Criteria c = new Criteria();Oh and then there's the fun with invalid locations that can either have isValid flag set to false or be null. But that is easy to get right. Finding out that a phone is giving you a bad provider is the real pain.
provider = LocationProvider.getInstance(c);
November 24, 2008
Oh, and internal GPS's will be supported!
It will be ready sometime this week.
For the version after that, I intend to try and dig a bit into saving and loading. It won't be perfect, but maybe, just maybe, it will work well enough for most cartridges.
sidenote: there is a midlet called "OpenWIG-blackmagic" sitting silently in my phone. it has all kinds of very evil features that should never have been born. muhahahaa!
sidenote2: The name "OpenWIG" sucks. I need to come up with a different one.
the official Players call "OnSet*" before performing the setting. I.e., when you set task.Complete = true, then the player first calls OnSetComplete and only after that actually sets the variable.
The OnSetComplete event in Srnčí důl (Roedeer deep), for some wicked reason, checks for this behavior: if task.Complete == false then (do stuff) So if at the moment of calling OnSetComplete, it's already Complete, there be failurez.
so I took the latest and greatest version and headed out, to try and play this cartridge, which is probably best one in CZ, and also the most complex one. So it was kind of baptism of fire for OpenWIG. Plus, this time, i was a member of a group equipped with Colorados, so i could check the behavior against the "right" one.
Found two bugs. In one, i could see all my virtual companions long after we left them behind in a dead zone. The other, at first i didn't think that it's a bug. I just completed a simple task ... then all the coloradists spent half hour trying to complete the very same task and failing spectacularly.
Both bugs were rather easy to find and remove.
First one: when a zone deactivates, it stops evaluating position events. That means that if you deactivate a zone in which you stand, as far as OpenWIG is concerned, you'll stand there till the end of days. Because it will never again check if you are inside or not. It just remembers the last state.
Fixed by forcefully removing a player from a deactivated zone.
Second: in the mentioned task, you have to carry an object outside of an invisible zone that envelopes the visible zone in which you start.
The respective event asks if invisibleZone:Contains(Player). Welll..... Yes, if the zones overlap, the player can be in more than one zone at once. That works fine. But when doing that, I apparently overlooked something: it doesn't go the other way around. Player is in multiple zones, but only one zone Contains() the player. So when i was in the inner visible zone, the event never found out that i was also in the invisible zone, and let me throw the task object away without hassle.
Fix was fairly trivial, i just needed to override zone's Contains method... ah, why would i write all this, you can figure it out.
...and for a time, it was good...
This cartridge also has that habit of deactivating zones that you stand in. But it turns out that when fixing the problem from Blair Witch, i overlooked something again.
There are two internal states for a zone: contain says whether you are present in (close to, distant from) the zone, and ncontain aka "next contain" says whether you are going to be present in the zone in the next five seconds, unless something happens. So most of the time it's the same as contain.
When "forcefully removing" the player, i reset the value of contain, but not ncontain. In the next timestep, zone realized that the player is going to be inside it real soon, and invoked the OnEnter event.
Which had a rather lengthy dialog. Which ended in a question. Which, if answered correctly, would deactivate the zone. Which would lead to another OnEnter... you see where this is heading.
Also i found out that even when the zone is inactive, quite a lot is going on in it.
So i put a stop to it, and that's the end of this nasty little bug.
...or is it?
November 3, 2008
It has its own functional file browser, and a pretty preferences screen. It mostly looks the way i described it a while ago.
No big changes on the player side of things, though, only some minor bugfixes.
- It's a beta and i didn't really test it (mostly because of the emulator stuff mentioned in last post). It just might be that it is severely broken in some stupid way and unfit for general use.
- It can no longer load files from JAR archive. That functionality might be restored, but as of now, i don't see any reason to do that.
UPDATE: ...CAN be very easy, IF you "specify the security domain" in NetBeans (and choose Manufacturer, for best results). All the problems mentioned below disappear, because the emulator stops asking questions. But if you want to test behavior on regular phone with no rights, you'll have to test on an actual phone - read on.
one: unlike most (if not all) phones, the emulator runs confirmation windows in the same thread as commandActions.
That means that if you try to do anything file-related from a commandAction, a security confirmation pops up and you can't dismiss it, because your command thread is (dead)locked waiting for the result of this confirmation.
Oh well, I made my file browser run the file commands in a background thread.
two: unlike all phones, the emulator wants a security confirmation for each and every method call on the FileConnection object. And it does not remember permissions. Therefore, listing a directory takes three confirmation clicks, and every time you want to re-read a file, another confirmation (which is likely to deadlock you, for reason one).
three: the emulator (at least version 2.5.2 in linux) creates a brand new tree structure for each run. And it appears that you can't choose what will appear in that structure. So if you want to test opening a specific file, you have to copy it into the proper location every time.
(although i remember that it behaved slightly differently on my notebook. i'll have to check that out too)
and all's good when the end's good, four: do not ever in your life try to "mount" parts of your filesystem into the emulator by linking them to the temporary filesystem root.
Upon exiting, the emulator happily descended into my directory and recursively deleted it. Fortunately, there wasn't anything irreplaceable, or i haven't noticed yet; but i lost a good hour of work.
Good to know, though, is that there is now a way to undelete files from an ext3 filesystem. Didn't help me, though; the software insisted on segfaulting. It's apparently a known problem with "large partitions on some 32bit systems".
October 6, 2008
True, the midlet would die on me when the GPS was forcefully disconnected ... but isn't that what midlets do? You take away their bluetooth connection, they die, right?
Wrong, apparently. And it wouldn't make sense, too - why should an application die just because of lost bluetooth connection?
For some reason, i started thinking about it yesterday, spent a good hour googling for the explanation and found none, so i finally gathered my courage and posted a question on SonyEricsson developer forums.
First answer solved my problem.
Two lessons to be learned here, apart from the "if google doesn't know that a problem exists, there's something wrong with the problem"...
First, Exception isn't the root of all that can go wrong. Throwable is. Replacing my Exception handling code to handle all Throwables revealed that there actually is a Throwable being thrown, and it's an OutOfMemory one.
Which also explained why the application died - when an OutOfMemory condition happens, dying is the sane thing to do. (Really. Trust me on this one.)
And second... well, not really that much of a lesson, except maybe "don't trust (foreign) code, even if you read it and thought that it's fine". That OutOfMemory was caused by a loop that continuously read from the bluetooth link and saved results into a ByteArrayOutputStream (which was rather stupid thing to do in itself, but oh well), until it found a newline.
Except that when the link died, stream did not vanish (that was handled, but honestly, what would you think if a stream just vanished from under your program? i wouldn't write something like that, but when i was reading it, it looked like proper error handling) and instead started reporting "end of stream". Which it did by returning -1.
The loop happily filled the byte array with -1's and happily ran out of memory in the middle of doing that.
Simple bug, simple fix.
Once you realize that it is a bug and not a status quo.
When i fired it up today, it didn't do anything. Plus it seemed that it only has parts of my most recent work. Or something.
Apparently, it has gone bitrotten from not being touched in few months. I'll have to dust it off, clean it up, look for the missing parts on my laptop and push out a version.
In other news, i've fixed the bug where the midlet would die instantly if you disconnect the GPS. Funny thing, i never thought that it's a bug in the application, i thought that it's just the way things are. Oh well.
July 24, 2008
Recently, DefectiveByDesign launched a campaign against iPhone, and generally, they fight against iPod and Apple products, because of the DRM employed in them.
You know what's sad?
Apple products rock. Seriously, they do. Did you ever hold an iPhone? Touched it? Typed on its wonderful multitouch-aware onscreen keyboard? That thing is damn sexy. And it Just Works (tm). And it's precise. There is no other device with the same degree of input precision on a touchscreen (yes, that is because of the multitouch capability, i.e. completely different principle of touch recognition), few devices with built-in gyrostat to detect device tilt, and none of them use it as well as iPhone does.
I mean, yes, there are "alternatives". They might be more powerful, more open, more compatible, better engineered or whatever.
And none of them holds a candle to iPhone's amazing usability and user experience. Plus, none of them can help you pick up chicks. iPhone can. Because iPhone is sexy.
I have a Cowon D2 player and i wouldn't trade it for an iPod. It has longer battery life, plays basically any format you throw at it, has a true DSP, works as mass storage, its firmware is updated regularly, blah, blah. Oh, and a touchscreen, of course. Yes, it outfeatures iPod in almost everything you can think of. But it's nowhere near as sexy.
And tell you what, average joe user does not give a rat's ass about features of D2. He has his Windows (or Mac) box with songs in mp3 format, and all he wants from a player is to actually play those songs. iPod does, and it does it sexy. Massively sexy.
I could go on for hours and hours about how Apple inovates while looking good. We all know it's true, even though some of us deny it. (Perhaps they are not Apple's original ideas, but it's Apple who shows them to the unwashed mass.)
The folks at DBD can bitch all they want, and they are right. The iPod and iPhone are bad, because they are all locked down and DRMed and stuff.
What is sad is that no other manufacturer takes on the challenge! Show me a single phone as cool as the iPhone. Show me a single player as cool as the iPod. Tell you what, there is none. iRiver Clix2 gets close, but not close enough.
DBD wants to pressure Apple into unlocking - "if you don't unlock, we'll go to the competition".
But the fact is that we won't, because iPhone is so damn cool.
Why, oh why, doesn't somebody pressure the competition into outsexying Apple?
July 12, 2008
For some f**ked up reason, this seems to be checked by defaut. At least on Linux, at least on imported pre-3 profiles.
Go ahead and uncheck the f**king thing for good! Bam there goes the caret, and your old & faithfull Home key brings you to the top of the page.
I can't believe how glad i am for having found this.
May 27, 2008
If you choose to obfuscate your midlet, you gain two things: the final size will be reduced, and the contents will be hard to disassemble.
But. If you turn on obfuscation in NetBeans, it will be used for your local debug builds as well. Due to that, you will lose debugging information, notably filenames and line numbers in stack traces.
two: Version strings must be handled with care.
My phone (SE K610i) refused to install a midlet with version "0.2.991", or anything longer than 6 characters. A different phone (Siemens CX65) installed a midlet with version "0.2.99sync1", which then mysteriously crashed with a NullPointerException that wasn't there with version "0.2.99".
This is bad magic. If anyone can explain it, please do.
three: From what i gathered, there is no way to list files contained in a JAR at runtime.
If you want to do that, create a text index at compile time and include it in a known location.
and four: Some older phones, even though MIDP 2.0, don't call Canvas.sizeChanged event.
Or maybe they do, but not (citing the spec) "at least once before the
Displayableis shown for the first time." [here] Or maybe they even do that, but don't specify the proper size.
So don't rely on it.
May 22, 2008
It has pretty navigation and crude but functional reading from filesystem. Everything important is on the project page, follow the link on your right.
...oh, and did I mention that we can now decode the GWC format? Well, we can. Feel free to load your memory card with cartridges and head outside.
Once again, I misinterpreted a part of the spec. You do need to reopen the InputStream if you want to seek() back, that's true, but. If you keep a FileConnection object for the file in question, you can call getInputStream() on it as many times as you want - the security prompt is shown only for first access, then the phone is supposed to remember that you already have permission to do that (and it appears that many (if not most) phones actually adhere to this).
I assume that this holds true for other functions on a FileConnection as well, but I didn't test that yet.
Oh, and that RMS caching idea I had was slow and stupid.
May 14, 2008
But, since i had to employ quite a bunch of clever tricks (let's call them that, at least until they all fail spectacularly), i need guinea pigs to test them on as many phones as guineapigly possible.
There will be a highly experimental version 0.2.99, as a precursor to actual 0.3.0 (which, according to my roadmap, should have at least partially usable filesystem access). I will probably release it sometime this night.
How gay is that? (pardon my french)
File I/O in Java has been traditionally too generic to be of any use (as are most parts of its standard library), but there was always a way to do what you need without excessive hassle.
Not true in J2ME.
Imagine that you are trying to read data from a file in a certain format. The file contains three distinct JPEG images. First one starts at offset 500 and has 1000 bytes. Second one starts at offset 1501 and has 1000 bytes as well, and the third one starts at offset 2502 and has also 1000 bytes, not that it matters here. Let's pretend that you know all this beforehand.
When you open the file, you get only an InputStream. If you want to read the second JPEG, you call stream.skip(1501), which, hopefully, brings you to the start of the data. However, unlike traditional seek(), this means that you are actually reading the first 1501 bytes and silently discarding them.
Let's just hope that it's optimized to do a seek() under the hood.
There are basically three options what to do next. In order of increasing stupidity:
- read() 1000 bytes into a byte array and work with that. If you happen to be using an API function expecting an InputStream, construct a ByteArrayInputStream over your array and use that.
- Implement your own LimitedLengthInputStream, which allows API functions to read up to 1000 bytes from the original stream, while catching all the crucial calls like close() in order to preserve the stream for further use.
- Hand over the original stream as-is and hope that it will "just work" with all the trailing garbage in the stream. Added value of this approach is that it effectively kills the stream and you need to open a new one.
The answer, as is common to Java, is surprisingly simple: you don't.
That's right. There is no reliable* way to go back in an InputStream. And, in J2ME, you have nothing but InputStreams. Bad for you, go open a new one.
* Well yes, there is this mark()/reset() function pair. In theory, you mark() a position in a stream and then reset() when you want to return to it. In practice, my phone said "mark/reset not supported".
(This was on a resource stream, maybe it will work on file streams, but somehow i doubt it. Plus, if it failed on one phone, it will fail on others.)
Oh, and about opening a new stream - did I mention that if your midlet isn't certified, each attempt to getInputStream() gives a pop-up warning to the user? As does basically any other operation on the filesystem?
That has enough material for a medium sized rant on its own.
So, for now, my solution is to walk through the file in one pass and save the individual files from it to a RecordStore. Then retrieve them at will.
The situation with pop-ups is not nearly as horrible as it appeared at first. See my new article on the subject.
What they are and how to make heads (but not tails) of them.
In today's GPS applications, you will see those values under "precision". Plus, if you happen to be writing a GPS application, you will see them in NMEA messages. When people compare their GPS receivers, they compare precision by DOP (at least I and my friend did).
But unless the application is pretty clever, it won't tell you the actual position error, only the DOP values. And, unless you as a programer are pretty clever, you can't read the actual position error from the GPS receiver! Which is slightly worse.
So. As some of you probably already noticed, DOP stands for Dilution of Precision. HDOP is Horizontal DOP (that is latitude and longitude), VDOP is Vertical (altitude). PDOP is Positional, which is an overall measure containing both horizontal and vertical, and TDOP is Time DOP, which doesn't seem to be good for anything useful.
In various sources you can find that DOP values are dimensionless and they express "geometric strength" of satellite configuration. And that lower is better.
Which is all nice, but doesn't tell you anything about what the hell those numbers mean.
So here goes: DOP values are coefficients. That is the most important thing to know about them. To obtain the position error range in some meaningful units, e.g. metres, you multiply the GPS receiver's precision by the DOP value.
That's all there is to it. Just take a DOP, multiply it by a known value, and there you go, now you have a meaningful number. Piece of cake, really. Why couldn't they tell you earlier, right?
Alright, i confess, that's not all. This is where the real fun begins.
For one, there seems to be no way of determining your GPS unit's precision. It certainly won't tell you through NMEA. So you can either read the full-of-propaganda manual, or take an educated guess.
My educated guess, based on GPS system properties and quality of today's receivers, is that average precision is 5 metres. In fact, my favourite GPS app, TrekBuddy, shows HDOP x 5 m as an error range. And for openWIG I'm going to do the same.
And for two, while some receivers (such as my NaviLock BT-451 with uBlox Antaris4 chip) exclude signal strength from DOP (that is technically correct approach, since DOP is geometric strength), others apparently count it in (technically wrong, but way more useful).
Either that, or SiRF starIII chip's geometric capabilities are way below Antaris's. What's a poor guy like me to think, eh?
Anyway, now you know what a DOP is. Have fun dealing with it, I certainly will.
May 7, 2008
Version 0.2.6, released some two weeks ago, doesn't have it all, but it has most of it: rather reliable zone detection and navigation, support for all basic Wherigo constructs (including Timers) and their events, images (preloaded in the jar file), PlayAnywhere features...
There are still quite a few loose ends in the Lua library, and i'm looking forward to tying them up. But unfortunately, there is a number of things whose priority is much higher.
One, navigation screen.
What we have now is something along the lines of "239,2m northwest". Which is nice, but not really precise. What we need is a graphical display with an arrow pointing towards the destination. Perhaps with some additional info - distance, target/current coordinates, speed, signal strength...
HandyGeocaching has it all, and i'll probably borrow most of the code. But to be honest, i dread the moment when i'll have to open the HG project again. Don't get me wrong, there's nothing with the code ... it's just that it's a Netbeans 5.5 project with visual designers, and it really really dislikes Netbeans 6. Or vice versa. Anyway, it's so ugly it's not even funny.
I'm actually thinking of writing my own navi display from scratch, just to avoid this :e/
Two, preferences and persistence.
This is where it starts to get funny - the midlet reached a level of complexity where some kind of preferences screen is necessary.
- GPS mode - Manual, Bluetooth, Built-in, Serial port.
The latter two are not done and i'll again have to dive into HG to fetch them. But that doesn't matter, bluetooth alone is enough fun. Last connection info should be persistent. And Manual mode has a few differences of its own.
- Navi screen refresh intervals.
Turns out that some phones of a certain vendor who shall remain nameless (cough cough Nokia cough) (and possibly some others, but that is yet to be seen) take a screen update as an instruction to scroll up. Which is all fun and games until somebody loses an eye, i mean, until you notice that the navigation info is on the bottom. And since the refresh interval is 1 second (synchronized to game engine ticks and presumed GPS ticks), unlucky owners of the affected phones can't really see the navi info.
If the screen was to refresh at, say, 10 seconds, their direction would not be so precise, but at least it would be readable.
- Last opened cartridge, information about cartridge directory, logs directory etc.
All those things that suddenly appear when you load and save files to the filesystem.
Which brings us to...
Three, filesystem access.
Yesterday I checked in a class that can load things from gwc cartridge. It is useful by itself, no doubt about that - at least it will enable people to save their cartridges into the jar without any external tools (apart from a zip, obviously, but everyone has zip today). But to make it really interesting, you have to be able to open things from a memory card.
That means filesystem classes plus browser gui plus persistence (see above) and a whole lot of other minor changes. Such as...
Four, brand new main-screen UI.
Because the current one doesn't really expect to have more than one cartridge. We need file browser (see above), cartridge selector with preview (splash screen, description etc.), Preferences screen (see above) and ability to close cartridge and open a new one.
And the best part is that this all needs to be done in one step, otherwise it won't make any sense.
Well, the navi screen i can release separately. But now i have gwc reading, not navigation. And gwc reading doesn't make sense without filesystem. Et cetera et cetera.
April 18, 2008
However, they now seem to be finished and working nicely on my SE K610i.
I even implemented "slow hysteresis" to compensate for GPS fuzz: when you are about to walk into a zone (where "zone" is not Wherigo.Zone, but a well defined area, e.g. the proximity space around a Zone, the inside of a Zone, or maybe the space so far away that it's even outside the DistanceRange), OpenWIG waits for five consecutive position samples that confirm your position in the new zone, and only then acknowledges the new position.
It's too late for me to be able to explain it in simple terms, but it actually is simple. And it works surprisingly well, at walking speed at least. But it is five seconds slow. I'll see what I can do about that.
Version 0.2.4 includes precise zone detection, distance detection, OnProximity and OnDistant events (last two from the "basic set" that is used in every other cartridge)
...and... (drums, please) navigation!
Yes, ladies and gentlemen, OpenWIG will now tell you exactly how far away are you and what direction you should take. The instructions are still rather crude, and they are displayed even when you are inside the zone (you can just disregard them, they don't mean anything inside a zone. i use them for debugging), but it does work.
Get it while it's hot!
April 10, 2008
In other news, OpenWIG 0.2.3 is out and it supports various kinds of Inputs, as well as few more dummy functions.
April 8, 2008
In addition to task support, it can now work with practically any cartridge code - although most cartridges will be unplayable anyway, because of missing functionality. See what is implemented and how to install your cartridge.
Since 0.2.1, it is possible to choose between manual input of coordinates and Bluetooth GPS device.
Misch's gwc decoder appears to be functionally complete, so it is just a matter of time when OpenWIG will be able to open and play cartridges compiled by official tools.
April 7, 2008
In other news: misch of geocaching.cz said that the gwc format doesn't look at all difficult to him. And that he would probably be able to decode it.
That means that OpenWIG will probably have support for original Wherigo Player cartridges, and that means that you will be able to play full-featured Wherigo on your mobile phone!
Isn't that just wonderful?
April 5, 2008
For whatever reason, Wherigo is designed in a way that perfectly maps to J2ME's gui handling. Commands, actions, dialog boxes, it all looks like somebody designed it to work in a midlet.
Except for one thing.
To quote Wherigo Advanced Concepts on the subject of MessageBoxes and Dialogs:
The Wherigo Player automatically cancels any message box that is buried by another user interface element. This means that the message box will not remain on the screen even after all other UI elements have been closed. However, the message box script (if one exists) will still execute. The author can override this by creating a condition to specify what should happen if the 'Cancel' button is pressed. These conditions will work regardless of whether the player intentionally presses 'Cancel' or the message box is automatically canceled by the application.Funny, I thought, this is going to be rather tough. You have to keep a return value from the message box, but anything can close it anytime. Hello and welcome aboard, race conditions. And what's even better, Lua VM is thread-unsafe. And it would be dumb to lock the whole VM for as long as the dialog is on, because that can be quite a long time.
On the other hand, it's quite a dumb idea to automatically cancel the dialogs when another comes. What if the user doesn't have time to read it? But ... who am I to judge :P
So let's restate the problem.
- Midlet UI works in non-blocking, fire-and-forget mode. Which is basically a good thing for an UI to do, but...
- ...we need to keep return values. Therefore we must block on the dialog.
- Anything can close the dialog anytime. I.e., another thread must tell the first one to continue.
- Lua VM is thread-unsafe, therefore the two must somehow arrange locking. Plus locking of the dialog threads, because a third dialog can come at any time and kill the two.
Strangely enough, it worked, and it worked really well.
Too well, actually.
I apparently missed some of the Advanced Concepts.
in Wherigo, a message box does not pause the cartridge. Instead, the game continues to run while the message box is still displayed on the screen.Just like midlets do. Fire-and-forget, anyone?
It actually does work like that. Dialog is put on screen and the script continues happily on its way. Return values? No sire. There are callbacks instead. Who could have thought?
(anyone who would try it in the builder ... oh well)
In fact, the very Wherigo Level Two cartridge I use for testing is relying on this "feature". Instead of if/elseif conditional blocks, it just displays messages over one another, stopping at the right time.
I was very surprised that OpenWIG showed more messages than the emulator. I even thought that it's a bug in the emulator, or some kind of unspecified if/elseif handling - which would be very, very bad.
Nope, none of that. Once again, I made the problem more difficult for me. If I didn't want to do it "properly", it would have been easier, and correct at the same time. Folks at GroundSpeak apparently don't do things the "proper" way, but the "practical" (easy for the programmer) way. I'll have to get used to it.
But auto canceling the dialogs? Come on. That is dumb. Really.
But "debugging" on the phone, without debugger at the ready, is a whole lot of fun. Mainly because on the phone, you don't have backtraces.
(Next time I might try OTA debugging, but right now I don't even know what exactly that is...)
The Lua VM must be triggering some bug in the emulator, I thought, too bad for me. And I released a testing version that worked perfectly on my phone.
Oddly enough, the Lua VM managed to trigger a bug in Nokia phones too. They would freeze or reset when attempting to run the midlet.
Solution? Let me quote a mail from Kristofer Karlsson, developer of Kahlua:
Someone previously mentioned that the function System.identityHashcodeIsn't that funny?
crashed for some strings such as "error"
Fortunately, the solution is simple: instead of System.identityHashcode(a), use a.hashCode().
Apparently, the identityHashcode function was slightly broken in older JVM's (and the WTK too, apparently. oh, and in Nokia phones. but oh well, i never liked Nokia anyway) and it couldn't cope with too many internalized strings.
Turns out, however, that the Wherigo Builder produces plain Lua source files. It can't be too hard to make that work on a mobile phone, now can it?
Well, let's hope not. Enter matejcik, who just recently bought a bluetooth GPS module to use with his phone. And whose idea of fun is, among other things, staying up late (or early, you might say) coding weird stuff.
Working name of the project is OpenWIG. As of now, it can run nearly all of Zooventure Level Two from the tutorial.
No GPS connectivity yet, but you can input coordinates by hand. I might keep this feature, as it would enable folks without GPS-enabled mobiles to play. Then again, I might not - you know how things go, cheaters are everywhere and this would only make it easier for them.
You can't load your own cartridges yet, and you will probably never be able to load gwc files, as the format is closed and I have no experience in reverse engineering. But once the loading functionality is there, I will probably set up a web service that would take your sources and compile them to OpenWIG-friendly format.
Heart of the midlet is called Kahlua and it is a Lua virtual machine for J2ME. It eats Lua 5.1 bytecode compiled files and works pretty damn well, considering its size. It is also fast enough on all phones I tested.
(Then again, it's not like the Lua scripts in the cartridges do a whole lot of work. Setting variables, conditional branch here and there, that's about it.)
And as an added value, the guy who wrote it was very helpful and the (two) bugs I reported were fixed immediately.
No download link today, but you can find some testing versions in this thread (in Czech).
Update: check out OpenWIG's Google Code page.