aboutsummaryrefslogtreecommitdiff
path: root/midend.c (follow)
Commit message (Collapse)AuthorAge
* Don't load too many states just because there's no STATEPOSBen Harris2023-01-15
| | | | | | | | | | | | | | | | | | | | | | | | | | If we start seeing state records in a save file (MOVE, SOLVE, or RESTART), we should already have seen STATEPOS, so emit an error if not. This avoids the situation where we overrun the end of the state array because we're continuing loading states in the hope a STATEPOS will come along. I've also added an assertion that we're not overrunning the state array for added paranoia. An earlier version of this fix just removed the test for data.statepos at the head of the loop, but that's wrong for a file that only has the initial state. This bug can be demonstrated by building Bridges with AddressSanitizer and loading this save file: SAVEFILE:41:Simon Tatham's Portable Puzzle Collection VERSION :1:1 GAME :7:Bridges PARAMS :13:7x7i30e10m2d0 CPARAMS :13:7x7i30e10m2d0 DESC :24:a4b4a1g1a2a8a4a4m2b2b3e3 NSTATES :1:2 MOVE :10:L1,0,4,0,1 MOVE :10:L1,0,4,0,2
* Range-check record lengths when deserialising gamesBen Harris2023-01-15
| | | | | | | | | | | | | | | "1999999999999999999999999999999999999999999999999999" as a record length should lead to an error, not a buffer overrun. (fun fact that was less obvious to me than it should have been: very large powers of ten are multiples of large powers of two, so that number is -1 mod 2^32) This bug can be demonstrated by building any puzzle with AddressSanitizer and then loading this save file: SAVEFILE:41:Simon Tatham's Portable Puzzle Collection VERSION :1999999999999999999999999999999999999999999999999999:1
* Add an assertion that all colours are within rangeBen Harris2022-12-14
| | | | Because I know I'm going to mess this up at some point.
* New backend function: current_key_label()Ben Harris2022-12-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This provides a way for the front end to ask how a particular key should be labelled right now (specifically, for a given game_state and game_ui). This is useful on feature phones where it's conventional to put a small caption above each soft key indicating what it currently does. The function currently provides labels only for CURSOR_SELECT and CURSOR_SELECT2. This is because these are the only keys that need labelling on KaiOS. The concept of labelling keys also turns up in the request_keys() call, but there are quite a few differences. The labels returned by current_key_label() are dynamic and likely to vary with each move, while the labels provided by request_keys() are constant for a given game_params. Also, the keys returned by request_keys() don't generally include CURSOR_SELECT and CURSOR_SELECT2, because those aren't necessary on platforms with pointing devices. It might be possible to provide a unified API covering both of this, but I think it would be quite difficult to work with. Where a key is to be unlabelled, current_key_label() is expected to return an empty string. This leaves open the possibility of NULL indicating a fallback to button2label or the label specified by request_keys() in the future. It's tempting to try to implement current_key_label() by calling interpret_move() and parsing its output. This doesn't work for two reasons. One is that interpret_move() is entitled to modify the game_ui, and there isn't really a practical way to back those changes out. The other is that the information returned by interpret_move() isn't sufficient to generate a label. For instance, in many puzzles it generates moves that toggle the state of a square, but we want the label to reflect which state the square will be toggled to. The result is that I've generally ended up pulling bits of code from interpret_move() and execute_move() together to implement current_key_label(). Alongside the back-end function, there's a midend_current_key_label() that's a thin wrapper around the back-end function. It just adds an assertion about which key's being requested and a default null implementation so that back-ends can avoid defining the function if it will do nothing useful.
* Assert that the back-end has provided a background colourBen Harris2022-12-06
| | | | | If we're going to refer to a specific colour, it seems appropriate to insist that it exists.
* Extra key mappings: '*' to undo and '#' to redoBen Harris2022-11-15
| | | | | This is what I keep expecting on my phone. Implemented in the mid-end since no backend currently uses those keys.
* Remove a couple of unused variables.Simon Tatham2022-11-10
| | | | These broke the overnight build, due to -Werror.
* Add a way for midend_process_key() to report whether it handled a keypressBen Harris2022-11-08
| | | | | | | | This adds a new bool * argument, which can be NULL if front ends don't care whether the keypress was handled. Currently they all do that. Currently, "undo" and "redo" keys are treated as not handled if there's no move to undo or redo. This may be a little too strict.
* Teach the mid-end about device pixel ratiosBen Harris2022-11-08
| | | | | | | | | | | | | | The device pixel ratio indicates how many physical pixels there are in the platonic ideal of a pixel, at least approximately. In Web browsers, the device pixel ratio is used to represent "retina" displays with particularly high pixel densities, and also to reflect user-driven zooming of the page to different text sizes. The mid-end uses the device pixel ratio to adjust the tile size at startup, and can also respond to changes in device pixel ratio by adjusting the time size later. This is accomplished through a new argument to midend_size() which can simply be passed as 1.0 in any front end that doesn't care about this.
* Build fix: take declarations out of for loops.Simon Tatham2022-10-21
| | | | The NestedVM build is still unhappy with this C99ism, unfortunately.
* Assert that everything written to a save file is printable ASCIIBen Harris2022-10-20
| | | | Apart from the newlines of course.
* Hex-encode non-ASCII random seeds in save filesBen Harris2022-10-20
| | | | | | | | | | | | | The developer documentation claims that save files are long ASCII strings. This is mostly true, but there's nothing stopping a user from entering non-ASCII characters as random seeds. The ASCII property of save files is useful, so encode seeds in hex before writing them unless they consist only of printable ASCII characters. Hex-encoded seeds are written under a new key, HEXSEED, to distinguish them from unencoded seeds. This means that old versions of the code won't be able to load encoded seeds, but that's not a great loss: seeds aren't generally portable between versions anyway.
* Add assertions that game descriptions consist only of printable ASCII.Ben Harris2022-10-20
| | | | | | That they are ASCII is implied by their inclusion in save files. Nothing requires an absence of control characters, but it seems polite to make them slightly readable.
* Add an assertion to check the format of encoded parametersBen Harris2022-10-20
| | | | | | | | | | | | Whenever the midend calls encode_params, it also checks that the result is a printable ASCII string that doesn't contain '#' or ':'. Parameter strings are embedded in save files, so they have to fit within ASCII. They can't contain '#' or ':' because those delimit the parameter section of a game ID. Nothing explicitly says they can't contain control characters, but those would be a particularly egregious violation of the recommendation that parameter strings be easy to type into a shell.
* Correct and enable the range check on statepos when loadingBen Harris2022-10-16
| | | | | | | statepos == 0 shouldn't ever occur in a save file because it indicates an uninitialised midend. OTOH statepos == nstates is normal. Also added an equivalent assertion when saving because Simon and I spent some time discussing whether it could happen.
* Add more validation to midend deserialisation routineBen Harris2022-10-16
| | | | | These are all pretty obvious and enforce constraints that would otherwise be enforced by segfault.
* Reinsert some missing screen-clears.Simon Tatham2021-04-27
| | | | | | | | | | | | | | | | I just introduced the 'first_draw' flag in the midend, which should force a screen clear whenever we draw a puzzle with a fresh drawstate. But in fact there were several places where the midend replaces the drawstate and I hadn't set that flag to true. In particular, a user just reported that when you press 'n' for a new game in an existing Magnets window, the new puzzle's clues are drawn, but any old clues in places where the new puzzle doesn't have one is not _un_drawn. (Because Magnets has no code to undraw a single clue - it never needs to!) Added a set of first_draw wherever we call new_drawstate, which should make this reliable again.
* Centralise initial clearing of the puzzle window.Simon Tatham2021-04-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | I don't know how I've never thought of this before! Pretty much every game in this collection has to have a mechanism for noticing when game_redraw is called for the first time on a new drawstate, and if so, start by covering the whole window with a filled rectangle of the background colour. This is a pain for implementers, and also awkward because the drawstate often has to _work out_ its own pixel size (or else remember it from when its size method was called). The backends all do that so that the frontends don't have to guarantee anything about the initial window contents. But that's a silly tradeoff to begin with (there are way more backends than frontends, so this _adds_ work rather than saving it), and also, in this code base there's a standard way to handle things you don't want to have to do in every backend _or_ every frontend: do them just once in the midend! So now that rectangle-drawing operation happens in midend_redraw, and I've been able to remove it from almost every puzzle. (A couple of puzzles have other approaches: Slant didn't have a rectangle-draw because it handles even the game borders using its per-tile redraw function, and Untangle clears the whole window on every redraw _anyway_ because it would just be too confusing not to.) In some cases I've also been able to remove the 'started' flag from the drawstate. But in many cases that has to stay because it also triggers drawing of static display furniture other than the background.
* Add method for frontends to query the backend's cursor location.Franklin Wei2020-12-07
| | | | | | | | | | | | | | | | The Rockbox frontend allows games to be displayed in a "zoomed-in" state targets with small displays. Currently we use a modal interface -- a "viewing" mode in which the cursor keys are used to pan around the rendered bitmap; and an "interaction" mode that actually sends keys to the game. This commit adds a midend_get_cursor_location() function to allow the frontend to retrieve the backend's cursor location or other "region of interest" -- such as the player location in Cube or Inertia. With this information, the Rockbox frontend can now intelligently follow the cursor around in the zoomed-in state, eliminating the need for a modal interface.
* Fix a handful of memory leaks in the midend.Simon Tatham2019-04-05
| | | | | I happened to notice these when running dominosa --generate under Leak Sanitiser.
* Use C99 bool within source modules.Simon Tatham2018-11-13
| | | | | | | | | | This is the main bulk of this boolification work, but although it's making the largest actual change, it should also be the least disruptive to anyone interacting with this code base downstream of me, because it doesn't modify any interface between modules: all the inter-module APIs were updated one by one in the previous commits. This just cleans up the code within each individual source file to use bool in place of int where I think that makes things clearer.
* Replace TRUE/FALSE with C99 true/false throughout.Simon Tatham2018-11-13
| | | | | | This commit removes the old #defines of TRUE and FALSE from puzzles.h, and does a mechanical search-and-replace throughout the code to replace them with the C99 standard lowercase spellings.
* Adopt C99 bool in the midend API.Simon Tatham2018-11-13
| | | | | | | | | | | | | This changes parameters of midend_size and midend_print_puzzle, the return types of midend_process_key, midend_wants_statusbar, midend_can_format_as_text_now and midend_can_{undo,redo}, the 'bval' field in struct config_item, and finally the return type of the function pointer passed to midend_deserialise and identify_game. The last of those changes requires a corresponding fix in clients of midend_deserialise and identify_game, so in this commit I've also updated all the in-tree front ends to match. I expect downstream front ends will need to do the same when they merge this change.
* Fix return value from newgame_undo_deserialise_read.Simon Tatham2018-06-21
| | | | | | | | | | | | | The read function used by midend_deserialise and friends is expected never to perform a partial read (the main deserialisation code always knows how many bytes it can expect to see), so it's specified to return simply TRUE or FALSE for success/failure, rather than the number of bytes read. This probably wasn't breaking anything, since in the case of deserialising from an internal memory buffer a short read could only arise due to an outright bug constructing the buffer. But now I've spotted it, I should fix it.
* Add a request_keys() function with a midend wrapper.Franklin Wei2018-04-22
| | | | | | | | This function gives the front end a way to find out what keys the back end requires; and as such it is mostly useful for ports without a keyboard. It is based on changes originally found in Chris Boyle's Android port, though some modifications were needed to make it more flexible.
* Forbid undo-of-new-game after midend_set_config.Simon Tatham2017-12-09
| | | | | | | | | | | | | | | | | | | | | | | | | | This is another situation in which the midend's state, at the time midend_new_game tries to save it, is not such as to generate a viable serialisation. In this case, it's because after the user enters a game id into the Game > Specific or Game > Random Seed dialog box, midend_set_config will have _already_ started overwriting important fields of the midend such as me->desc and me->seed, before midend_new_game is even entered. In fact this caused an assertion failure (thanks to Lennard Sprong for reporting it), because one of those fields was set to NULL, so the resulting serialisation buffer was incomplete, leading to a deserialisation error later. But I was lucky: if I hadn't been, the serialisation might have been complete, and valid according to the deserialisation code, but would have contained a mixture of the new game's description and the old one's move chain, which would surely have had far stranger results. For the moment, I'm fixing this by simply not storing a serialisation in that situation. Perhaps a nicer approach might be to store one before starting to overwrite midend fields, and then have midend_new_game swap it in if it's already been generated. That way you _could_ still undo past an action like this. But preventing the assertion failure is a good start.
* Permit redoing past an undone New Game action.Simon Tatham2017-11-18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Now, when we undo a New Game by deserialising the stored version of the previous game, we start by serialising the _current_ game into a second serialisation buffer in the midend. Then, if the user tries to redo back past that undo action, we can re-serialise the earlier game and re-deserialise the newer one. A few users had complained (with various degrees of politeness) at the lack of this ability, because in true xkcd #1172 style, it broke their workflow. Specifically, if you were a fan of holding down 'u' to undo all the way back to the start of your current game, you'd have overshot into the previous game and then have no way to return to the game you wanted to be at the start of. This slightly changes the previous interaction of Redo with New Game. Previously, New Game would save the entire undo chain of the serialised game, including anything forward of the current position; so if you took actions move1,move2,undo,newgame then the serialised game state would include both move1 and move2 with the current position between them; hence, an undo would restore the old game to a position just after move1, and then a redo action would re-enact move2. Now, midend_purge_states is called before serialising the old game, so in that scenario move2 would be completely lost as a side effect of the new-game action. (Just as it would be if you made any _other_ undoable move in that situation.) Conversely, making a move in the old game after you've undone back into it will now wipe out the record of the later game, so you can't redo into it any more.
* Refactor to make me->newgame_undo a small struct.Simon Tatham2017-11-18
| | | | | | | | | The three related buf/len/size fields are now a sub-structure of the main midend, and the serialise/deserialise functions that address those fields now take a pointer to that sub-structure rather than to the midend as a whole. This will make it easy for me to drop in a second substructure alongside it, for redo, and reuse those read and write functions by just changing the context pointer.
* Fix assertion failure if you Undo right at startup.Simon Tatham2017-10-06
| | | | | | | | | The initial call to midend_new_game() was creating a partial serialisation containing no game states at all, which meant that if your first UI action was an undo operation, the game would try to deserialise that and complain that it was incomplete. Now we detect that in advance and don't create a useless serialisation in the first place.
* Make the code base clean under -Wwrite-strings.Simon Tatham2017-10-01
| | | | | I've also added that warning option and -Werror to the build script, so that I'll find out if I break this property in future.
* Assorted char * -> const char * API changes.Simon Tatham2017-10-01
| | | | | | I went through all the char * parameters and return values I could see in puzzles.h by eye and spotted ones that surely ought to have been const all along.
* Return error messages as 'const char *', not 'char *'.Simon Tatham2017-10-01
| | | | | They're never dynamically allocated, and are almost always string literals, so const is more appropriate.
* Use a proper union in struct config_item.Simon Tatham2017-10-01
| | | | | | This allows me to use different types for the mutable, dynamically allocated string value in a C_STRING control and the fixed constant list of option names in a C_CHOICES.
* New name UI_UPDATE for interpret_move's return "".Simon Tatham2017-10-01
| | | | | | | | | | | | | Now midend.c directly tests the returned pointer for equality to this value, instead of checking whether it's the empty string. A minor effect of this is that games may now return a dynamically allocated empty string from interpret_move() and treat it as just another legal move description. But I don't expect anyone to be perverse enough to actually do that! The main purpose is that it avoids returning a string literal from a function whose return type is a pointer to _non-const_ char, i.e. we are now one step closer to being able to make this code base clean under -Wwrite-strings.
* Make newgame_undo_buf 'char *', not 'void *'.Simon Tatham2017-10-01
| | | | | This fixes a compile error under -pedantic at the point where we do pointer arithmetic on it.
* Forbid undo of new-game if it would change the params.Simon Tatham2017-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The newgame_undo data was being saved on every call to midend_new_game, including the one just after midend_set_params when a new puzzle preset was selected. So you could select a new preset from the menu, and then press 'u', and the midend would _try_ to undo that operation and restore the previous game with a different set of parameters. This would do the wrong thing in the front end, because front ends in general will not be expecting that a change of game parameters might result from an arbitrary keyboard event - they won't be expecting to have to call the function that moves the highlight in the game-type menu, for example, and they _certainly_ won't be expecting that a window resize might be necessary in response to a random keystroke. One possible response would be to fix all the front ends so that they _are_ prepared for either of those consequences of a keystroke event, and then it would be possible to undo not only the New Game menu option and the 'n' key but also undo any selection of a preset from the game-type menu, or even a full re-customisation of the game settings. But that would be quite an upheaval even in _my_ front end collection, and also probably be awkward for downstream front ends, so until I'm convinced of the value of going to all the effort, the simpler approach is just to disallow undoing a new game in those situations. (This does mean that re-selecting the _already active_ game preset from the type menu will be treated as an undoable new-game event, which I think is an acceptable UI oddity.)
* Style tweaks to the newgame_undo patch.Simon Tatham2017-10-01
| | | | | | | | | | | | | | | | | | | | | | | I've renamed the new midend variables to match my usual naming convention of using 'size' for the total buffer size and 'len' for the amount of currently used space (and a couple of other variables to match those in turn), partly for consistency and also because the name 'midend_undo_used' made me half-expect it to be a boolean. The buffer itself is called 'midend_undo_buf', again to avoid making it sound like a function or flag. Buffer growth is still geometric but less aggressive (factor of 5/4 each time instead of 2), in the hope of wasting less memory on low-RAM platforms; newgame_undo_deserialise_read should have been static, and now is; newgame_undo_buf is initialised using NULL rather than 0 so it doesn't look like an integer, and is freed with the rest of the midend. And I think we _should_ enforce by assertion that midend_deserialise didn't return an error, because there's no reason it ever should in this situation (unlike when reading from a file, where the user might have provided the wrong file or a corrupted one). This immediately allowed me to spot a bug in the existing deserialisation code :-)
* midend: Allow "new game" to be undoneIan Jackson2017-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | It is annoying when one intends to choose "restart" and chooses "new game" instead. Right now, the puzzle one wanted to try again is discarded. To fix this we are going to have to save a lot more information than a normal game state. Handily, we already have the serialise/deserialise machinery. The advantage of using this is that the previous game is easily saved in its entirety, including its own undo history, and also probably in a more compact format. The (de)serialisation interface is rather clunky for use with a memory target. Sadly none of the existing implementations of a resizing memory array have been conveniently brought out into puzzles.h, and I think that that's beyond the scope of what I wanted to do here. We don't serialise the new game undo serialisation data. So loading/saving doesn't preserve any "new game" undo, as does "new game" twice (and as does context switching on a Palm Pilot). Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
* midend_deserialise: accept an extra validation function.Simon Tatham2017-10-01
| | | | | | | | | | | | This will let me do a 'conditional deserialisation' operation, in which we fully decode the serialised data and then (assuming that gave no errors) decide whether or not to actually install it based on some arbitrary condition. I don't think there's any possible use for the extra check function _outside_ midend.c, so I've left the API for front ends as it is; the externally visible midend_deserialise() doesn't have the new parameter, and only midend_deserialise_internal() includes it.
* midend_deserialise: keep deserialised data in a struct.Simon Tatham2017-10-01
| | | | | | | | | | Lots of the local variables in midend_deserialise are now fields of a structure which contains everything that is _going_ to be written into the midend once we finish validating it all. This makes it easy to keep all that data together, and (in future) pass it to other functions all in one go. No functional change.
* deserialise: use the right one of {,c}params.Simon Tatham2017-10-01
| | | | | | | | | | | | | | | | | | | | | | | | The serialised game stores a long-term and a short-term parameter structure, which correspond to me->params (the thing that gets used by the next New Game command) and me->curparams (the thing that _was_ used to generate _this_ game). So data relevant to the current game ought to be validated against the latter, but in fact I was accidentally passing the former to several validation calls. I think this probably avoided causing a problem because typically params and cparams don't differ very much: the usual reason why they're not the same is that somebody has manually entered a game description involving an incomplete description of the parameters (lacking generation-specific details like difficulty level), but by the very fact that those incomplete descriptions have to contain _enough_ information to understand a specific game description, copying just those parts of the description into the long-term params structure makes the two similar enough that validation won't fail. However, testing an upcoming patch which calls midend_deserialise at a more difficult moment (specifically, just after midend_set_params, meaning that the two params structures can now differ _arbitrarily_) reveals my error. Fixed to use cparams where that's the right thing.
* Generate special fake keypresses from menu options.Simon Tatham2017-09-20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This fixes an amusing UI bug that I think can currently only come up in the unpublished puzzle 'Group', but there's no reason why other puzzles _couldn't_ do the thing that triggers the bug, if they wanted to. Group has unusual keyboard handling, in that sometimes (when a cell is selected for input and the key in question is valid for the current puzzle size) the game's interpret_move function will eat keystrokes like 'n' and 'u' that would otherwise trigger special UI events like New Game or Undo. The bug is that fake keypress events generated from the GUI menus looked enough like those keystrokes that interpret_move would eat those too. So if you start, say, a 16x16 Group puzzle, select an empty cell, and then choose 'new game' from the menu, Group will enter 'n' into the cell instead of starting a new game! I've fixed this by inventing a new set of special keystroke values called things like UI_NEWGAME and UI_UNDO, and having the GUI menus in all my front ends generate those in place of 'n' and 'u'. So now the midend can tell the difference between 'n' on the keyboard and New Game from the menu, and so Group can treat them differently too. In fact, out of sheer overcaution, midend.c will spot keystrokes in this range and not even _pass_ them to the game back end, so Group shouldn't be able to override these special events even by mistake. One fiddly consequence is that in gtk.c I've had to rethink the menu accelerator system. I was adding visible menu accelerators to a few menu items, so that (for example) 'U' and 'R' showed up to the right of Undo and Redo in the menu. Of course this had the side effect of making them real functioning accelerators from GTK's point of view, which activate the menu item in the same way as usual, causing it to send whatever keystroke the menu item generates. In other words, whenever I entered 'n' into a cell in a large Group game, this was the route followed by even a normal 'n' originated from a real keystroke - it activated the New Game menu item by mistake, which would then send 'n' by mistake instead of starting a new game! Those mistakes cancelled each other out, but now I've fixed the latter, I've had to fix the former too or else the GTK front end would now undo all of this good work, by _always_ translating 'n' on the keyboard to UI_NEWGAME, even if the puzzle would have wanted to treat a real press of 'n' differently. So I've fixed _that_ in turn by putting those menu accelerators in a GtkAccelGroup that is never actually enabled on the main window, so the accelerator keys will be displayed in the menu but not processed by GTK's keyboard handling. (Also, while I was redoing this code, I've removed the logic in add_menu_item_with_key that reverse-engineered an ASCII value into Control and Shift modifiers plus a base key, because the only arguments to that function were fixed at compile time anyway so it's easier to just write the results of that conversion directly into the call sites; and I've added the GTK_ACCEL_LOCKED flag, in recognition of the fact that _because_ these accelerators are processed by a weird mechanism, they cannot be dynamically reconfigured by users and actually work afterwards.)
* Call game_id_change_notify_function after deserialisation.Simon Tatham2017-09-14
| | | | | | | | | | | | | | That's a case in which the current game IDs have changed, so the midend ought to be calling the front-end function (if any) that notifies it when that happens. The only front end of mine that was affected by this missing call was the Javascript one, which uses that callback to update the 'Link to this puzzle' links below the game canvas - but, of course, that front end didn't ever call midend_deserialise until this month, so no wonder I never noticed before. (But downstream front ends might be affected too, for all I know.)
* Work around non-compliant sprintf().Franklin Wei2017-04-30
| | | | | | | | Rockbox's sprintf() lacks the ability to left-justify a string. Fixed by adding a copy_left_justfied() function to misc.c. This is a new version of this commit, as the previous version broke saving!
* Rework the preset menu system to permit submenus.Simon Tatham2017-04-26
| | | | | | | | | | | | | | | | | | | | To do this, I've completely replaced the API between mid-end and front end, so any downstream front end maintainers will have to do some rewriting of their own (sorry). I've done the necessary work in all five of the front ends I keep in-tree here - Windows, GTK, OS X, Javascript/Emscripten, and Java/NestedVM - and I've done it in various different styles (as each front end found most convenient), so that should provide a variety of sample code to show downstreams how, if they should need it. I've left in the old puzzle back-end API function to return a flat list of presets, so for the moment, all the puzzle backends are unchanged apart from an extra null pointer appearing in their top-level game structure. In a future commit I'll actually use the new feature in a puzzle; perhaps in the further future it might make sense to migrate all the puzzles to the new API and stop providing back ends with two alternative ways of doing things, but this seemed like enough upheaval for one day.
* End victory flash on new game and restart game.Jonas Kölker2015-10-14
| | | | | | | | | | | | Net provides the best demonstration of why. Complete a game of net, then press N while the victory flash is playing: then the victory flash keeps playing on the new game board. (Tip: save a game which but for a redo is completed, then you can reproduce this repeatedly without having to complete a new game each time.) The flash timer reset code is placed together with the animation timer reset code, because the two are conceptually related. Note that midend_restart_game resets animations via midend_finish_move.
* Reset midend animation counters on starting a new game.Jonas Kölker2015-10-14
| | | | | | This is already done in midend_restart_game via midend_finish_move. If it's good enough for restarting a game, it ought to also be good enough for starting new games.
* Remove a redundant line of code.Jonas Kölker2015-10-14
| | | | | Setting me->anim_time = 0.0 right before calling midend_finish_move is redundant, since midend_finish_move itself sets me->anim_time = 0.
* Don't stop animations when restarting an already restarted game.Jonas Kölker2015-10-14
| | | | | | | | Restarting a game that is already in the restarted state is meant to be a no-op. It stopped animations. Don't do this. Also, given that midmidend_restart_game called midend_stop_anim twice, the invocation we remove was redundant.
* Stop animations on a new game, no matter how it is started.Jonas Kölker2015-10-14
| | | | | | | | Animations were stopped if a new game was initiated with a keyboard shortcut (n, N, Ctrl-N), but not via menu items such as presets or custom configurations, nor (perhaps not a problem) on starting the program. Fix this, so that animations are stopped on a new game no matter how the new game is started.