Copyright (C)1997,2001,2002 by Eric Sunshine <sunshine@sunshineco.com>

BUGS.txt

BUGS
----
[OPENSTEP]
- On Win9x, when a WM_SETTINGCHANGE is received, the program usually locks up.
  This happens once in a while when only one window is open.  It happens almost
  every time when multiple windows are open.  This problem does not seem to
  have anything to do with SokoWindow-windows.m's catching of WM_SETTINGCHANGE,
  since the lock-up occurs even when SokoWindow-window.m is entirely commented-
  out.  This problem does not occur on NT/2000.  Oddly enough, other AppKit
  applications do not seem suffer from this problem, however I am unable to
  discover how SokoSave differs from those applications, nor why it is subject
  to this problem.
- If a playback session is triggered in response to the undo- or redo-push
  buttons, and if the user is keeping the button depressed, the button stops
  sending its action messages .  This is annoying for the user since it
  interrupts the otherwise continuous undo or redo (assuming the user is
  holding down the button).  Figure out a way around this problem.
[NEXTSTEP]
- If a playback session is triggered in response to the undo- or redo-push
  buttons, and if the user is keeping the button depressed, the button stops
  sending its action messages .  This is annoying for the user since it
  interrupts the otherwise continuous undo or redo (assuming the user is
  holding down the button).  Figure out a way around this problem.

ISSUES
------
[OPENSTEP]
- On Win9x, the approach of using notifications to drive the animation doesn't
  seem to work very well.  The animation runs somewhat slowly until the user
  aborts playback or the playback ends normally, at which point animation
  speeds up considerably for a second or so.  [I tried the alternate approach
  of performing animation in a tight loop where the event queue is checked
  manually, but performance was no better.  This leads me to believe that the
  problem lies either with the AppKit code which interfaces with the Windows
  event mechanism, or the Windows event mechanism itself.  This problem does
  not occur with Windows NT/2000.  At any rate, the animation rate is only
  mildly slow, so it doesn't affect playback dramatically.  Furthermore, as of
  v15, there is a native port of SokoSave to Windows, so this minor performance
  problem is not worth additional effort, and I consider the issue closed, at
  least for the present.]

UNREPRODUCEABLE
---------------
[CBUILDER]
- At least five times (out of many, many play sessions), a puzzle was solved
  with a new high score but the "New Score" panel failed to launch.  In several
  cases, the "New Score" panel finally launched some time later, after other
  actions had been taken with the application.  For instance, in one case the
  "Save As..." panel was launched and dismissed before the "New Score" panel
  finally popped up.  In another case, another application was made active and
  then SokoSave was activated again.  Only after re-activation, did the "New
  Score" panel suddenly pop up.  This problem has occurred only on Windows 9x;
  never with NT/2000.  I (Eric) have never been able to reproduce this problem
  in a situation where it could be debugged.  Furthermore, I spent several days
  tracing through the SokoSave and VCL source code in an attempt to diagnose
  how this could possibly happen, but was unable to come to any satisfactory
  conclusion.  There are only two "impossible" cases which might cause this
  behavior.  First, if the "idle" handler is not being invoked in a timely
  fashion, then we would see this behavior.  Second, if the idle handler is
  being invoked, but one of the conditions which delay launching of the "New
  Score" panel is still true, then the launch would indeed be delayed.
  However, tracing through the SokoSave and VCL source code did not illuminate
  any way that either of these conditions could possibly arise.  Since I was
  unable to determine which "impossible" condition is causing the problem, or
  why the problem would occur for any other reason; since I can never reproduce
  the bug in a debugging situation; and since I don't know how else to approach
  the problem at this time, I am moving this bug report to the "unreproduceable
  bug" section.  (This is unfortunate because, unlike other unreproduceable
  bugs, this one has occurred more than once; several times during one testing
  session, in fact.)  I did make the small compromise of eliminating most of
  the criteria which delay launch of the "New Score" panel.  Whether or not
  this change will eliminate the problem behavior, however, is anyone's guess.
- Using a slightly old sokocore which employed Paul's original solved-check
  logic, SokoPuzzle failed to note that a puzzle had become solved.  All
  subsequent attempts to reproduce the problem by replaying the exact same
  moves failed.  This problem was only experienced under Win9x, though I can't
  think of any reason why it would be specific to that platform since the
  problem would have had to have been in the low-level SokoPuzzle code which
  should be independent of any platform-specific problems.  Tracing through the
  code did not reveal any possible cause for this failure.  At any rate, I
  (Eric) have since completely re-written the solved-check logic in SokoPuzzle
  to be more robust.  It's possible that the re-write may have addressed this
  problem (but that's just a guess).
- While playing a game, in which I had used the undo-push button, I switched to
  another application.  Several seconds after switching away from SokoSave,
  TSokoBoardForm::repeat_button_mouse_up() was triggered.  Since this method
  had already been triggered correctly earlier, it threw an exception (since no
  mouse-down had preceded it.  [I have no explanation for why this happened,
  however the assertion in repeat_button_mouse_up() is not really needed, and
  the program is not harmed by having this method called unnecessarily, so I
  simply removed the assertion.]
- After adding the toolbar, I experienced a problem where closing all windows
  failed to end application.  I had opened several new and saved games via the
  toolbar buttons, and had also launched the scores panel and the help file.
  One particularly suspicious symptom was that the fly-over highlighting of the
  toolbar buttons stopped getting erased at some point.  Perhaps the VCL had
  thrown some internal exception which caused the buttons to behave in this
  manner and which prevented "close" messages from being sent to the various
  windows as they were closed(?).

FIXED
-----
v19
[GENERAL]
- Crate path-finding code is returning invalid paths in some cases.  [The
  problem was a race condition of sorts where minimum-cost calculations at a
  given distance from the origin were utilizing costs which had already been
  calculated for the same distance for neighboring cells, however, these
  calculations should have only been considering costs calculated during the
  previous (distance-1) iteration.] (v19 bug)
- Now that crate path finding has been augmented to discover paths which
  overlap upon themselves to any depth, crate-related path finding stacks are
  not large enough, thus there is a possibility that the path finding code
  could overflow the stack.  Currently, the stacks are only large enough for
  (width*height) cells, but, since cells can be visited multiple times (once
  via each edge) during the flood-fill, the stacks should have an approximate
  capacity of (width*height*maxedges). (v19 bug)
[CBUILDER]
- On slow displays the animation with the C++Builder port tends to flash a lot
  for Hexoban and Trioban puzzles under Win9x.  The specific problem is that
  Win9x is first painting the hexagonal or triangular cell black before
  blitting the actual tile on-screen.
- SokoSave.iss places RCS files from help/images/RCS into SokSetup.exe if the
  setup compiler script is run from the developer's RCS package, rather than
  the plain source code package.  [Unfortunately, InnoSetup does not provide a
  way to both recurse into subdirectories and filter out certain entries, such
  as RCS directories.  Therefore, for now, I removed the recursion from the
  rule which copies the help files into the installer program.]
[NEXTSTEP]
- SokoScores.m in the NextStep port has broken logic.  It is formatting the
  integer in one buffer and then returning a pointer to the other buffer.
- TableScroll buffer count is not used correctly in SokoScores for NextStep.
  Should have two buffers for each column, not just two buffers total.  [Rather
  than waste 512 bytes for these static buffes, I eliminated the "buffer count"
  optimization altogether.]

v18
[GENERAL]
- Wrong scores are recorded after loading a saved game and achieving a new high
  score for one of the metrics.  [Problem was that it was sending the "best"
  scores for all metrics to the SCORES file entry for this game, rather than
  the actual scores for each metric for this game.] (v17 bug)
- Least-cost player movement in crate path finding seems to be broken.  It is
  not choosing the path with least player-movement cost.  [Problem was that
  shortest_crate_path() was not tracking sufficient information.  It was only
  tracking the minimum player-movement cost from cell to cell but did not know
  from which previous cell that cost arose.  This was a problem during the
  final path gleaning step when walking backward over the flood-fill if it came
  to a cell at which player-movement cost was the same from more than one
  approach.  It was unable to decide which approach was less costly for the
  push which it had just walked over backward.] (v11 bug)
- Triangle hit detection is incorrect. (v18 bug)
- Assertion failure often when moving crates in Trioban puzzles:
    SokoPuzzle.c:3248: failed assertion `IS_EMPTY(BOARD(dst_row, dst_col))'
  [This was a problem with the "direction alias" optimization.  This
  optimization was supposed to cut in half the amount of additional work needed
  to perform crate path finding for Trioban puzzles by taking advantage of
  already-computed player movement costs.  Unfortunately, the code for this
  optimization added significant complexity to the overall crate path finding
  implementation, was highly fragile (I never did get it working with 100%
  correctness), and was quite confusing.  Therefore, I eliminated that
  optimization, thus also eliminating this problem.] (v18 bug)
- Path finding for Trioban puzzles is broken.  The computer will find invalid
  paths which it will then be unable to use.  Also fails to find valid paths.
  [This was a consequence of the now-removed "direction alias" optimization.]
  (v18 bug)
- Re-enacting history often with a Trioban puzzle often throws an assertion:
    SokoPuzzle.c:2639: failed assertion
        `(is_push && IS_CRATE(dst_ch)) ||(!is_push && IS_EMPTY(dst_ch))'
  [This was a simple copy/paste error in the calculation of `oddcol'.]
  (v18 bug)
- Auto-detection of even-on-even/odd-on-even for Hexoban puzzles doesn't work
  when comments appear in the file before the puzzle data. (v17 bug)
- Important leading whitespace gets trimmed off Trioban puzzles by
  shrink_puzzle(), thus reversing the sense of North / South facing triangles.
  This happens with fm001.tsb and fm002.tsb, for example. (v18 problem)
- Auto-undo/redo detection is not working for Trioban puzzles.  Moving in the
  exact opposite direction of the last move does not perform the associated
  undo/redo.  [Problem was an optimization which limits the number of
  directions which are examined when computing player movements.  For Trioban
  player movement, only need to examine three directions, rather than all six.
  For simplicity, was using three directions which worked regardless of whether
  or not triangle was north- or south-facing.  Unfortunately, these three
  directions were not reversible.  That is, the auto-undo/redo could not detect
  that an oppposite move had been made.  To fix, now chooses three directions
  based upon north/south-facing attribute of cell, such that the directions are
  reversible.] (v18 bug)
- With Trioban puzzles, the focus score not computed properly when a saved-game
  is loaded.  With adptri001, the computed value is much too high.  [Unlike
  LuRd solutions for square and hexagonal puzzles from which the focus score
  can be computed directly, this can not be done for triangular puzzles.  For
  triangular puzzles, it is also necessary to know the north-/south-orientation
  of the player's starting location.  Fortunately, it is possible to intuit
  this information from the saved puzzle data.] (v18 bug)
- Directional buttons in UI are disabled and mostly non-functional for Trioban
  puzzles. (v18 issue)
- Need more thorough interpretation of rules for enabling directional buttons
  on control panel for Trioban puzzles.  Although currently accurate, they do
  not allow the user to move in straight lines by repeatedly pressing the same
  button.  To address this issue, will need to enable more buttons at any given
  time. (v18 issue)
- SokoPuzzle leaks a file descriptor each time it loads a puzzle (but not when
  loading a saved game). (v17 bug)
[CBUILDER]
- There are errant semi-colons in the descriptions of some of the file types
  listed in the new/open panels. (v17 bug)

v17
[GENERAL]
- Unable to select crate in hexagonal mode for all valid cases.  [Was only
  checking four approachs; now checks all six.]  (v17 bug)
- Unable to use quick-push-crate for all diagonal cases in hexagonal mode.
  [Rewrote from scratch.  Now totally generic and can handle any number of
  directions.] (v17 bug)
- When loading adp001.hsb, complains "unable to locate player".  [Problem was
  with the puzzle itself.  As represented on David Skinner's web page, the
  puzzle breaks convention of using even characters on even rows and odd
  characters on odd rows.  It was using the inverse rule.  To gracefully deal
  with future instances of this problem, I made the puzzle parser more robust.
  It now dynamically determines which scheme is used by the puzzle and adjusts
  accordingly.] (v17 issue)
- Up & Down buttons probably should not be enabled in hexagonal mode.
[CBUILDER]
- Square grid painting is leaving holes as other windows are dragged across a
  game board.  This is a problem with the clipping-rect optimization in the
  Paint() method. (v17 bug)
- Some square grids are not being drawn entirely.  Portions of the grid are
  entirely clipped.  Observation indicates that the puzzles with missing chunks
  are those which are narrower than the minimum width of the SokoBoard window.
  This is a problem with the clipping-rect optimization in the Paint() method.
  (v17 bug)
- When wildly dragging a puzzle window over other puzzle windows (possibly over
  gh001), receive the following assertion failure:
      col >= 0 && col < p->data->col_count; SokoPuzzle.c:572
  Further observation: This is happening anytime the saved gh001 is painted;
  even when first opened.  Further: This occurs for any puzzle which is smaller
  than the minimum puzzle board size.  [Was using BoundsRect when should have
  been using ClientRect.] (v17 bug)
- For hex puzzles, there is a lot of flicker when animating.  [The flicker was
  a result of using a TImageList for drawing.  When changed so that drawing is
  done directly with TCanvas and TBitmap, the flicker went away.] (v17 issue)
- For hex puzzles, mouse hit detection is broken.  [Problem was caused by a
  couple shadowed variables.] (v17 bug)
- When using large fonts and other large element sizes on Windows, the controls
  on SokoBoard and SokoPrefs don't adjust properly.  For instance, on the
  preference panel, when using a large font, the various controls overlap.
  David W. Skinner also reports that the "P" in "Pushes" gets truncated.  This
  is probably also a problem with font sizes. (v15 problem)
- Playback slider has focus rectangle when puzzle is first opened. (v17 issue)
[OPENSTEP]
- The icon names in CustomInfo.plist are applicable only to Windows since they
  use the .ico extension.  Generalize this so that the information in this file
  can be used by other platforms. (v15 issue)

v16
[OPENSTEP]
- +[SokoBoard openPuzzle:] calls SokoPuzzle_destroy(0) which, naturally,
  crashes.  Should not be calling this function at all. (v15 bug)
[NEXTSTEP]
- +[SokoBoard openPuzzle:] calls SokoPuzzle_destroy(0) which, naturally,
  crashes.  Should not be calling this function at all. (v15 bug)

v15
[GENERAL]
- Crashes at termination in many cases if a SokoSetting was instantiated.
  Sometimes happens if SokoSetting was a static object.  Other times happens if
  SokoSetting was heap-allocated.  This object, or the C++ wrapper, must be
  corrupting memory.  [Problem was that all SokoCore classes failed to allocate
  memory for their `data' member even though they were writing to memory
  referenced by `data', as though memory had been allocated.] (v15 bug)
- If SokoWrap.h is used by a header which defines a class and by one which does
  not define a class, then an ordering dependency arises, where other files
  must include the class-defining header first.  The problem is that SokoWrap.h
  maintains state between inclusions in order to create C++ wrappers.  When a
  non-class-defining header includes SokoWrap.h, the state machine gets left at
  the incorrect state and fails to work correctly if then included by a
  class-defining header.  [Problem was that SokoWrap.h was being included by
  headers which did not actually require C++ wrappers.  They were included it
  merely for the "export" directive.  To work around this problem, I introduced
  a "soko_export" directive into SokoDefs.h, so these headers no longer need to
  include SokoWrap.h.] (v15 issue)
- The SokoPuzzle constructor wrapper can result in objects being leaked if an
  old _SokoPuzzle is returned rather than a new one.  This can happen if the
  requested puzzle is already open.  The problem is that the C++ wrapper class
  is unconditionally stored in _SokoPuzzle::info.  However, if a puzzle is
  already open, and an existing _SokoPuzzle is returned, the class referenced
  by the existing "info" will get blasted and replaced by the new SokoPuzzle
  reference.  [Added special macros to SokoWrap.h to allow a factory method to
  return a previously instantiated wrapper object.  Also added special macros
  to allow a method or factory to return a delegate reference.] (v15 bug)
- The SokoPuzzle initializer calls the delegate to refresh all cells and to
  refresh controls.  This fails when the delegate is wrapped by a C++ class
  since the "info" pointer for the C++ class is not yet set at that point, thus
  the program crashes.  [Fixed by having all constructors accept an "info"
  argument which gets installed in the class' "info" variable by the
  constructor, rather than the constructor merely setting the "info" variable
  to null.] (v15 issue)
- None of the wrapped SokoCore headers are properly include-protected.  They
  all check for the SokoBlah_h macro, but none of them ever sets the macro.
  (v15 issue)
- The process of opening a single puzzle uses more path buffers than the number
  allocated to soko_get_path_buffer().  I had to increase the cache size to 48
  to overcome this problem.  This is quite a waste of memory.  It would
  probably be a better idea to simply allocate strings as needed and then allow
  the client to free the string when no longer needed.  It should be possible
  to have the number of buffers adjust dynamically.  This could be accomplished
  by providing a function which should be called by a path manipulation routine
  before allocating buffers and another function when done allocating buffers.
  These functions can be called multiple times and should remember the call
  depth.  As long as the call depth is not zero, then they should never re-use
  any buffers.  Once the call depth falls to zero, they can re-use buffers
  (though it would still be nice to avoid immediate re-use of the last `n'
  buffers).  [Instead, I created a new memory pool object, SokoPool, which gets
  passed to functions which have need of path buffers.] (v15 issue)
- When opening a saved game which is not stored in the official "save"
  directory, a brand new instance of SokoBoard is created each time the saved
  game is opened, rather than simply noting that it is already opened and
  ordering front the opened instance.  On the other hand, opening a saved game
  from the official "save" directory does correctly note that the game is
  already open.  [This was a logic error in SokoPuzzle::find_open_puzzle().  It
  was unconditonally applying soko_save_filename_for_puzzle_name() even though
  the passed-in path already represented a "save" filename.] (v15 bug)
- When I opened a saved game from the Library/SokoSave directory in my home
  directory (known by $HOME), the window's title was not collapsed to
  $(HOME)\Library\SokoSave.  Can not reproduce this, however using Save As...
  does not collapse the name in the title bar.  [This was a bug in
  SokoPuzzle::save_as().  It was not calling soko_collapse_path().] (v15 bug)
- SokoBoard::open_puzzle_for_level() fails to open an existing saved-game if
  default path settings are used.  [Problem was that it tried checking for a
  file named $(SokoUser)/blah.sokoave.  soko_puzzle_name_for_level() was not
  expanding the returned path.] (v15 bug)
- When default path settings are used, the window title for files opened from
  the "save game" directory is collapsed to "$(SokoUser)/blah.sokosave".
  However, the window title for new puzzles is not collapsed; for example:
  "c:/My Documents/SokoSave/blah.sokosave".  I'm not sure which type of title
  is preferable.  I think that I probably prefer the long form (though it does
  tend to get _quite_ long) because it gives the user sufficient information to
  actually find the file in the filesystem, whereas the opaque $(SokoUser)
  token provides no information.  Anyhow, this indicates a more low-level
  problem.  Specifically, SokoPuzzle is not being consistent about how it
  stores the saved-puzzle name internally.  Also ensure that everything that
  makes use of the save-name treats it consistently (either collapsed or in
  long form).  For instance, SokoBoard::open_puzzle() calls
  SokoPuzzle::find_open_puzzle() with a long path.  [Fixed several places in
  SokoPuzzle where the paths were not being handled consistently.  Now, both
  the puzzle path and the save path are always stored in long, normalized form
  internally.  Also decided to display full path in window title for reason
  stated above.] (v15 issue)
- Puzzle does not become "unsolved" when a crate is moved away from a goal
  square after already having been solved.  [Rewrote solved-check logic in
  SokoPuzzle so that it now correctly reflects all changes to solved state.
  Also, solved-check is now much more optimal; no longer needs to scan entire
  puzzle each time it needs to check if puzzle is solved.]
[CBUILDER]
- TSokoBoardForm becomes visible too early (as soon as it's created).  [Needed
  to clear the Visible flag at design-time.] (v15 bug)
- SokoBoard window can be maximized.  This doesn't make sense since the window
  can not otherwise be resized.  [Fixed by disabling that button in the form
  designer.] (v15 bug)
- When specifying files on the command-line, all but one would be opened by
  SokoApp.  [Problem was that documentation for ParamCount() was not clear.  It
  actually returns (argc - 1).] (v15 bug)
- Merging the menu from the main application onto the PuzzleBoard only works as
  long as the original window which got the merged menu is still open.  Once
  that window closes, other windows which are opened later do not get the
  merged menus.  This also causes big problems with the new code which updates
  the Window menu since that code tries manipulating the menu after it has
  disappeared.  [Original problem was that I was not calling TMenu::Unmerge()
  when form was destroyed, so menu items from application's menu were getting
  free along with the SokoBoard (presumably).  Next problem was that I was
  calling Unmerge() from OnDestroy which was (apparently) too late, and the
  menu items had already been destroyed by that point.  Moving the Unmerge()
  call to OnClose fixed the problem.] (v15 bug)
- Code to activate a window from the Window menu, though it correctly handled
  minimized windows, was not respecting if window had been maximized before
  minimization. (v15 problem)
- If the About or Scores panel has been launched but minimized, then clicking
  on the About or Scores menu item again fails to deminimize the window.
  (v15 problem)
- Open Scores window.  It correctly appears on Window menu.  Close Scores
  window.  It is correctly removed from Window menu.  Open Scores window again.
  It does _not_ appear on Window menu.  Furthermore, at this point, Close
  button on Scores window fails to work.  (Alf-F4 also fails to work.)  Same
  problem afflicts the About and Preferences windows.  [Problem was caused by
  previous fix for windows not being deminimized when appropriate menu items
  were invoked.  I was restoring the window from a minimized state directly
  using raw Windows API and bypassed the VCL, so the VCL did not know that the
  window had been placed back on-screen.  Consequently, none of the VCL's
  bookkeeping was getting done, nor was it calling SokoForm::DoShow() which is
  the hook for placing the window's title on the Window menu.] (v15 bug)
- The code to position the Open dialog in the "save" directory the first time
  launched doesn't seem to be working. (v15 bug)
- It appears that the Open dialog gets reset to the location of the New dialog
  which seems rather odd. (v15 bug)
- New dialog is not respecting the user's last chosen directory when next
  launched.  The code to respect the choice is there, but it's not working
  properly.  The Open dialog, on the other hand, does seem to be respecting the
  user's last choice of directory (except as noted above). (v15 bug)
- New dialog is not pre-selecting the puzzle file even though the code is in
  place to do so.  [I forgot to soko_expand() the name before giving it to
  TOpenDialog.] (v15 bug)
- Very first time user launches program, Info panel is correctly launched
  automatically.  However, when user closes info panel, entire program exits.
  Upon thinking this over, the Info panel is probably being registered as the
  "main" application form, rather than SokoApp form having that status.  [Now
  uses `new' to create the various forms instead ofTApplication::CreateForm().
  This way, TSokoAppForm is guaranteed to become "main-form" since it is the
  only one created with CreateForm() -- the method which actually sets the
  TApplication::MainForm property.] (v15 bug)
- I broke keyboard-based movement when I "fixed" the OnKeyDown handler in
  SokoBoard after discovering that it was duplicating functionality of
  SokoPuzzle::key_action().  Alternately, I may have broken
  SokoPuzzle::key_action(), which I also re-wrote.  [Problem was just a bad
  conditional in SokoBoard.] (v15 bug)
- Borland's resource compiler doesn't respect the order ICO files are specified
  in the SokoSave.rc file.  It appears to be sorting them alphabetically.  This
  sorting causes the wrong icons to be used for the various registrations, as
  well as for the application icon.  [Rather than naming the ICON resources
  directly, I used #defines to give them bland, but ordered names.  In source
  code, I can use the verbose defined macro names, but as for the resource
  compiler, it should respect the ordering as specified by the names.  This is
  not the most wonderful solution, but it will suffice for now.] (v15 issue)
- The "Minimize All" and "About SokoSave" menu items both use Ctrl+H for a
  shortcut.  Ctrl+H should be owned by "Minimize All", so choose something
  other for "About SokoSave". (v15 bug)
- TSokoBoardForm::handle_app_idle() neglects to check if a drag session is
  active before launching the New Score panel.  Also try to account for any
  other types of input before launching that panel.  For instance, don't launch
  panel if repeating directional button is down.  Don't launch panel if key is
  down (though this is more difficult to detect). (v15 issue)
- I experienced a case where I opened a saved game; then started a new game;
  then tried opening another saved game.  When opening the second saved game,
  the Open panel was rooted at the wrong directory; it was rooted in the
  new-game directory.  [This was yet another copy/paste error.  Was saving the
  directory setting from the wrong panel.] (v15 bug)
- Now that file associations are set up automatically, double-clicking on a
  SokoSave file correctly opens that file if the program was not already
  running.  However, if it was running, then double-clicking on a file only
  brings the application forward, but does not open the second file.  Add logic
  to handle this case.  [Added another message named "SokoOpenFile" which is
  handled by TSokoAppForm::app_message().  Global string atoms are used to pass
  the pathnames from the command-line of the second instance to the first
  running instance of the program.] (v15 issue)
- When selecting a bunch of SokoSave files in the Explorer and "opening" them
  not all of them are opened.  The problem is that Explorer launches an
  instance of SokoSave.exe for every single file being opened.  Each opened
  instance tries communicating with the first instance in order to tell it to
  open the desired file, but unfortunately some messages appear to get lost.
  Need to employ a sure-fire mechanism for communication between instances in
  order to avoid such problems.  [Created the SokoRendezvous class which is
  100% accurate at rendezvousing and passing command-line arguments from
  secondary instances to the first running instance.  Employs a mutex for
  synchronization (unlike the original rendezvous() function which only used
  the mutex to see if another instance was running).  Uses WM_COPYDATA to send
  command-line arguments, since WM_COPYDATA is less resource intensive than the
  global string atoms used previously.  Also, it's easier to ensure proper
  cleanup of data passed via WM_COPYDATA.] (v15 problem)
- If the user double-clicks on a puzzle or saved game from the Explorer, and if
  that puzzle is already opened but minimized, then the program fails to
  deminimize it. (v15 problem)
- If SokoBoard fails to open a puzzle for some reason, it clears its `puzzle'
  instance variable by setting it to zero, however the SokoBoard destructor
  then dereferences the null pointer when it tries calling
  SokoPuzzle::abort_playback(); (v15 bug)
- When I mentioned a single, but invalid, argument on the command-line at
  launch time, something popped up on screen momentarily, and then the program
  simply exited.  Should at least display an error message and then try opening
  the "default" puzzle (the code to do this is already there, so I don't know
  why it didn't happen).  [The SokoBoard destructor would ask the application
  to terminate if it was the last open puzzle.  This behavior was
  contraindicated, however, at launch time since SokoApp might still
  successfully open another puzzle after the one that failed to open.
  Therefore, terminating the application was the wrong behavior at that point
  in time.  To fix, moved the check for last open board to SokoBoard's
  form_close() method, instead.] (v15 bug)
- It is possible to crash SokoSave given the following scenario.  1) Launch.
  2) Minimize the puzzle window.  3) Launch a second instance with the name of
  a SokoSave file.  3) Close the window of the just-opened file.  4) Launch
  another instance with no file.  5) Crash.  [TScreen::ActiveForm was being set
  to null in this case, and TSokoAppForm::secondary_launched() was
  unconditionally dereferencing that null pointer.] (v15 bug)
- Now that each window's parent handle is the desktop window rather than the
  global application handle, there is are several cases where the global
  SokoSave button appears on the taskbar even after I have manually hidden it
  in app_restored().  If the application was hidden, and a second instance is
  launched _with_ a SokoSave file as a command-line argument, then the first
  instance is correctly restored but the application-global taskbar button is
  not hidden.  Another strange manifestation of this problem is that if I send
  the global application button an SW_HIDE message _before_ manually making the
  hidden windows once again visible, the button never hides.  If I send SW_HIDE
  after restoring visibility of the other windows, then the button hides
  correctly except for the case stated above (where another file is opened).
  Even another manifestation of this problem is that when a window is closed,
  the global taskbar button suddenly appears.  [The problem was that, once I
  changed the windows' parent handles to the desktop window,
  TApplication::UpdateVisible() suddenly began unhiding the global taskbar
  button much more frequently; often any time a TSokoForm window was hidden or
  shown.  I worked around the problem by having TSokoForm manually hide the
  global taskbar window in these cases (as well as having TSokoApp hide it, as
  usual). (v15 problem)
- The installed version of SokoSave for Windows (Borland) is not respecting any
  of the command-line arguments when launched by double-clicking on a SokoSave
  file.  It completely ignores these files.  However, if launched from the
  command-line, then it respects the arguments.  [It turns out that the problem
  was that, once installed, in "Program Files", whenever Windows launches the
  program, all command-line arguments are sent to it as "short" pathnames.
  Thus, for example, "c:\My Documents\24.sokomaze" was arriving as
  "c:\mydocu~1\24~1.sok".  Naturally, the .sok extension was not recognized.
  Another related issue is that soko_collapse() wasn't able to collapse these
  "short" names since it was looking for the long versions.  Finally, SokoSave
  was not reporting an error message to the user when it failed to open the
  file, so it appeared that it was ignoring the arguments, but in fact it was
  trying though failing to recognize the file extension.  To work around the
  problem, I added enrich_pathname() to SokoBoard.cpp.  This function
  re-creates the file's original "long" name from the "short" name via a rather
  convoluted process (since Windows does not provide any convenient way of
  achieving this).] (v15 problem)
- Not generating an error message for the user when SokoPuzzle_new() fails to
  recognize a file extension. (v15 bug)
- Launch the installed, optimized SokoSave by double clicking.  Then launch
  SokoSave from the command-line with a SokoSave file as an argument.  When the
  file represented by the argument is opened, the window does not have the main
  form's menu merged in.  (In other words, the Window and Help menus are
  missing.)  In fact all new windows after this point are missing those menus.
  The problem is that the VCL believes that it should unmerge the menu
  automatically, even though it should only be happening when I do it manually
  when the SokoBoard closes.  This anomalous behavior started occurring only
  after I changed TSokoForm to set the window's parent handle to the desktop
  window rather than the global application handle.  Apparently, however, the
  VCL expects the window's parent handle to be the global application handle,
  and when this is not the case, it starts behaving in bizarre fashions.  In
  fact, while tracing through the VCL code in the debugger for a different
  problem (where the VCL was revealing the global taskbar button), I saw
  TApplication explicitly unmerge the menu even though this wasn't supposed to
  happen.  To work around this problem, I will probably end up ditching use of
  the menu merging mechanism altogether.  [The menu merging approach was
  abandoned.  The hidden SokoApp form no longer contains a menu.  This approach
  was a fairly simple solution except for the "Window" menu which must now be
  managed manually and individually for every menu-bearing form, rather than
  being managed just once via the global instance from the hidden SokoApp
  form.] (v15 bug)
- The captions of the email and web-page fields on the Info panel are not being
  set programmatically, though the URLs are being set. (v15 bug)
- Opening a solved save-game for which high scores have not been recorded (that
  is, recorded_moves and recorded_pushes are zero) results in a crash.
  Technically, this case should never arise, however I ran into a case where I
  solved the puzzle, but either SokoPuzzle or SokoBoard did not note that it
  was solved, so the score never got recorded (see "unreproduceable bug"
  above).  I then saved this solved, but unrecorded puzzle; with the result
  that the "recorded" scores were zero.  When the saved game was later loaded,
  this caused a crash since SokoPuzzle tried to launch the New Score panel at
  puzzle load time (before SokoBoard was entirely initialized) since it saw
  that the puzzle was solved and that the new score was better than the
  recorded score.  [I actually discovered this bug while running a slightly
  outdated version of C++Builder SokoSave.  As it turns out, I had already
  fixed this bug in a later version as a side-effect of re-engineering the
  puzzled-solved check logic.  In the new version, SokoPuzzle never tries
  recording a new high score at load time; it only records high scores in
  response to actual player actions.] (v15 bug)
- Launching the new-puzzle and open-puzzle panels does not abort playback.
  (v15 issue)
- A SokoBoard instance may be added to the idle-list via two different code
  paths, yet the low-level list manager only adds the instance once.  Later,
  one code path may remove the instance from the list even though another code
  path believes that the instance is still on the list.  (Technically, this
  should never happen given the current usage of the idle list, but the code
  should be more robust and protect against this case, anyhow.) (v15 bug)
- SokoBoard's destructor neglects to remove the instance from the idle list.
  This can result in messages from the static idle handler being sent to a
  deleted instance.  (Technically, this should never happen, but there have
  been cases where the new-score panel was supposed to launch at idle but
  failed to do -- see "unreproduceable bugs" -- so the destructor should remove
  the instance from the idle list, just in case.) (v15 bug)
- If the undo/redo history menu items are used during playback, an exception is
  thrown.  These items should be disabled during playback. (v15 bug)
- clean.bat doesn't work with 100% correctness on Win9x. (v15 bug)
- Fails to build with BCB5.  (I've been using BCB4.)  Apparently, need to
  define the C-processor macro NO_WIN32_LEAN_AND_MEAN in the project file in
  order to avoid compiler complaints about duplicate symbol from shlobj.h.
  (v15 issue)
[OPENSTEP]
- SokoBoard holds an extra reference to `cellMatrix' which it never releases.
- During playback animation, the playback scroller still responds to events for
  some reason even though it has been disabled (and even has a disabled
  appearance).  [This is a bug in the AppKit.  Sending -setEnabled:NO to the
  NSSlider from within a method invoked as the slider's action doesn't actually
  disable the slider, though it does redraw in a disabled state.  To work
  around the problem, I ended up having to disable the slider the first time
  SokoBoard's -advancePlayback: is invoked.] (v15 issue)
- Window title handling is inconsistent.  Furthermore, when a game is saved to
  a well-known location, such as $(HOME) or $(SokoUser), the title is displayed
  in collapsed form, but should be displayed as a full path.  A saved game file
  which is opened from the home directory may appear as "~/name.sokosave" or
  "/full/path/name.sokosave".  [The first problem was caused by a bug in
  SokoPuzzle::save_as() where it was still collapsing the path before saving it
  as the save_file_name.  I had pored over SokoPuzzle pretty carefully to try
  to unify puzzle_file_name and save_file_name so that they are always
  maintained as normalized, expanded paths, but apparently I missed this one.
  Fixed by expanding the path, rather than collapsing it.  As for the second
  problem, there doesn't seem to be anything I can do about it since NSWindow's
  -setTitleWithRepresentedFilename: method is collapsing the path down to
  tilde-form itself in certain cases but not others.  Since this is occurring
  inside the AppKit, I'm not going to bother trying to come up with a
  solution.] (v15 bug)
- The new lengthier web site URL on the Info panel does not display properly.
  It's getting truncated. (v15 issue)
- It appears that the small version of the application ICO file is not being
  utilized by Windows.  Instead, a blurred image, which is presumably the large
  ICO scaled down, is used in each window's title bar and in the task bar.
  This is probably an AppKit shortcoming, where it is setting only the window's
  "large" icon via WM_SETICON.  Also need to fix this for the global
  application icon which is shown on the taskbar button when the application is
  hidden.  [I added an NSWindow category which publish -finalizeIcon.  This
  method must be called by clients _after_ a "window handle" has been allocated
  for the NSWindow.  Currently, this is only done for SokoBoard since it is the
  only window which actually displays an icon.  All other modules use panels;
  none of which display icons.  As for the taskbar button when the application
  is hidden, I was hoping that -[NSApplication applicationHandle] was the
  handle representing the taskbar button for a hidden application, but this is
  not so.  I was unable to find any method for obtaining access to the window
  handle representing the taskbar button, therefore, there doesn't seem to be
  any way to "fix" that particular icon.]
- On Windows, the "Minimize" menu item does nothing but beep.  This is true for
  both YellowBox and OpenStep.  [According to the NSWindow documentation, if
  -performMiniaturize: can't miniaturize the window for "some reason", it calls
  NSBeep().  Apparently, that is what is occurring, but there is absolutely no
  indication as to why it thinks that the winodw can't be miniturized.  I
  worked around the problem by connecting the "Minimize" menu item to
  -miniaturize: instead of -performMiniaturize.] (v15 problem)
- On YellowBox/Windows, the "Minimize" menu item incorrectly displays as
  "Minimize Window" even though it doesn't say that in the nib.  On
  OpenStep/Windows, the menu item is "Minimize" as expected.  [Interestingly,
  this problem disappeared after I changed the Minimize item to send
  -miniaturize: instead of -performMiniaturize:.  Apparently, YellowBox only
  checks for the one selector when forcibly changing the item's title.]
- If user changes application-font size, URL fields on Info panel use the new
  size rather than staying at 12-point.  This can cause ugly display anomalies
  on the Info panel.  [Fixed by also employing the NSFontAttributeName key in
  the attributed string.] (v15 bug)
- On Windows, invoking -setDocumentEdited: on the Preference panel causes the
  panel to suddenly become listed on the "Window" menu.  By default, panels are
  not supposed to be listed on the Window menu unless the programmer has
  explicitly sent -setExcludedFromWindowsMenu:NO (which I have not done).  This
  is a bug in the AppKit.  Furthermore, the AppKit fails to remove the item
  from the Window menu when the panel is closed, thus it is left dangling.
  [Worked around this AppKit bug by providing custom implementations for
  -isDocumentEdited and -setDocumentEdited: in SokoPanel.]
- On Win9x, miniaturizing a SokoSave window causes it's size to become kookoo
  when later deminiaturized.  This is almost certainly some bizarre consequence
  of the window not being resizeable.  Resizeable windows don't experience this
  problem.  This problem does not occur on NT/2000.  [To work around this
  problem, on Windows 9x, SokoWindow now automatically adds the "resizeable"
  flag to the window's style mask at creation time.  It is then up to the
  client -- SokoBoard, in this case -- to ensure that the user can't actually
  resize the window.  Therefore, SokoBoard now sets the minimum and maximum
  size limits of the window to enforce this constraint.  The only mildly
  negative side-effects of this change are that the resize-cursors appear when
  the mouse hovers over a window edge, and that the "maximize" button in the
  title bar is enabled.  Fortunately, however because SokoBoard contrains the
  minimum and maximum window size, neither of these operations is actually
  allowed, thus these are only minor aesthetic annoyances.  The fact that the
  miniaturize function now works correctly is far more important than the minor
  aesthetic issues.]
- The menu validation is not properly disabling History menu items during
  playback.  If the user invokes one of those items, then the program crashes
  with an assertion failure (since history actions should not be invoked during
  playback). (v15 bug)
- SokoURL incorrectly dispatches the URL if a mouse-down is received, which is
  outside the active URL rectangle, if it is immediately followed by a mouse-up
  without any intervening mouse movements.  It should only dispatch the URL if
  the mouse is hoving over the active URL rectangle at mouse-up time.  [Bug was
  that code incorrectly assumed that the mouse-down was always inside the
  active URL rectangle, even though the SokoURL's frame might be larger than
  the active rectangle.  Upon mouse movement, the "mouse inside" flag was
  correctly recomputed taking the active URL rectangle into account, which is
  why this bug only manifested if the mouse-up was received with no intervening
  mouse movement.] (v15 bug)
[NEXTSTEP]
- Target of OK button on SokoNewScore panel is not connected. (v15 bug)
- textDelegate of editable text fields on SokoNewScore are not connected.
  (v15 bug)
- NextStep Slider control has same bug as OpenStep NSSlider where disabling the
  control in its own action method fails to actually disable it (even though it
  draws as disabled).  [Now uses the same patch as the OpenStep port; disables
  the slider the first time -advancePlayback: is invoked.] (v15 issue)
- NextStep Slider control doesn't draw itself as enabled after it gets
  re-enabled by -endPlayback, even though it actually is re-enabled.  [This
  problem disappeared as a side-effect of fixing the above problem.]
- No text field is selected on the SokoPref panel by default. (v15 bug)
- Neither player nor crate dragging work.  [Forgot to add mouse-dragged masks
  to SokoBoard's event-mask in -beginDrag.] (v15 bug)
- Scores in TableScroll are sorted differently depending upon how panel is
  first launched.  If launched via +launch, then the scores are properly
  sorted.  If launched via +solved:moves:pushes:, then the scores appear in
  SCORES-file order.  [TableScroll forgets all sorting information upon receipt
  of the -renewRows: message which is invoked when a new score is added, thus
  now manually sends a -sort message, afterward.  This problem cropped up after
  I changed MiscTableScroll to use lazy-mode rather than eager-mode.] (v15 bug)
- Launching the Info panel a second time results in three PostScript error
  messages being spit at stderr.  At this point, the "hand" cursor ceases to
  appear when hovering of a URL field, though the tracking rectangle for
  underlining and coloring is still functional.  Presumably, there is one
  message per cursor and/or tracking rectangle (for the three URL fields).
  This is occurring because the Info panel is "one shot", and the PostScript
  gstate in which the tracking rectangle lives is being destroyed when the
  panel gets closed.  Therefore, SokoURL's invocation of -discardTrackingRect:
  tries to discard a non-existent object.  This is an AppKit bug, in my
  opinion.  The OpenStep AppKit does not have this problem.  [Rather than
  taking the obvious route of removing the one-shot attribute, I added
  -startTracking and -stopTracking methods to SokoURL.  SokoInfo now invokes
  these methods at appropriate times, for instance when the window is about to
  close, and when the window gains and loses keyboard focus.] (v15 problem)
- The menu validation is not properly disabling History menu items during
  playback.  If the user invokes one of those items, then the program crashes
  with an assertion failure (since history actions should not be invoked during
  playback). (v15 bug)
- SokoURL incorrectly dispatches the URL if a mouse-down is received, which is
  outside the active URL rectangle, if it is immediately followed by a mouse-up
  without any intervening mouse movements.  It should only dispatch the URL if
  the mouse is hoving over the active URL rectangle at mouse-up time.  [Bug was
  that code incorrectly assumed that the mouse-down was always inside the
  active URL rectangle, even though the SokoURL's frame might be larger than
  the active rectangle.  Upon mouse movement, the "mouse inside" flag was
  correctly recomputed taking the active URL rectangle into account, which is
  why this bug only manifested if the mouse-up was received with no intervening
  mouse movement.] (v15 bug)

v14
[GENERAL]
- SokoBoard's +preparePathFinding:: is not settings `preallocated_area' in all
  cases.  The result is that the shared flood-fill buffers get re-allocated
  each time a puzzle is opened, which leads to memory corruption if a small
  puzzle is opened while a large puzzle is still open, since the large puzzle's
  flood-fill routines expect the larger shared buffers to still be present.
  (v11 bug)
[OPENSTEP]
- OpenStep: SokoBoard's -makeMatrix is leaking the 'protoCell'. (v10 bug)
- OpenStep: SokoFile's expand_macro() is leaking a mutable copy of the
  `alphanmericCharacterSet'. (v10 bug)

v13
[GENERAL]
- Springiness of "Animate" and "Stop" buttons is botched.  This got messed up
  when I swapped the location of these two controls.  (v11 bug)

v12
[GENERAL]
- SokoBoard's -preparePathFinding:: declares `preallocated_area' without a type
  (thus it defaults to `int').  (v11 bug)

v11
[GENERAL]
- SokoBoard's -appendMoveToHistory:isPush: calls realloc() with a null pointer.
  Some realloc() implementations do not handle this case well.
- unpack_binaries.sh fails to set the permissions of the extracted files.
[OPENSTEP]
- On MacOS/X Server 1.0 (Rhapsody/YellowBox), Undo and Redo menu items are
  always disabled.  [This was caused by Apple's introduction of the
  undocumented -undo: and -redo: methods into NSWindow which caused NSWindow to
  intercept these messages before they ever arrived at SokoBoard.  Fixed by
  connecting these methods directly to the -undoMove: and -redoMove: methods in
  SokoBoard, instead.]
[NEXTSTEP]
- NextStep: MiscTableScroll delegate methods in SokoBoard fail to return a
  value.

v10
[GENERAL]
- "New Score" panel lets user resize in both dimensions, but it only makes
  sense to resize horizontally.
- When opening a saved game with the window deferred, the NSSlider/Slider
  doesn't get drawn.  [SokoBoard was incorrectly & unnecessarily calling
  -sizeToFit on the slider.  For some reason on deferred windows this has the
  side- effect of setting the slider's frame-size to (0,0).]
- New and Open panels fail to restrict the allowed file types.  [Problem was
  that they were using the inherited -setRequiredFileType: method which seems
  to be ineffectual in NSOpenPanel/OpenPanel.  Now explicitly uses
  -runModalForDirectory:file:types:.]
- Valid saved game reports corrupted number of moves/pushes when reopened.  To
  reproduce:
  1) cmd-n start a game
  2) cmd-S save the game (without making any moves)
  3) cmd-w close the game
  4) cmd-o open the game (get error message about corrupt moves/pushes)
- When the history in a saved file has length 0, -[SokoBoard loadSaveFile]
  incorrectly calls malloc(0).
- The preference panel needs to grab a new Level value each time it is
  launched.  Otherwise it might have an old value in it since the Level can
  change just by playing the game.
- Opening a saved game can report "corrupt recorded values".  To reproduce:
  1) solve and save game
  2) undo two or more times
  3) make a move which is *not* in the history
  4) save game
  5) close game
  6) re-open game (get error message about corrupt recorded values)
  The problem is that at step 3, the history gets truncated.  Yet the load
  function asserts that the "recorded" moves & pushes are less than the
  history-length, but after the truncation this assertion is not valid.
[OPENSTEP]
- New "level" is not getting recorded when a level is completed.
- When the game is solved, the AppKit always throws an exception stating that
  there is a non-positive window number.  This happens after the event handling
  the movement which caused the game to become solved, whether it's done with
  -performSelector:afterDelay:, or with notifications.  Apparently this only
  happens if the score panel has not yet been launched.  [This is a bug in
  MCGHTableScroll, calling -lockFocus in -drawRow: without first checking
  -canDraw.  I originally worked around it temporarily by making the window
  non-deferred.  Ultimately I replaced MCGHTableScroll with NSTableView.]
- Under Rhapsody, SokoBoard fails to compile on account of the deprecated
  -makeObjectsPerform:withObject:.
- Under Windows, the lazy loading of the first maze in
  -applicationDidBecomeActive: never takes place since this message does not
  get sent until there is a window on-screen.  Must make special-case for
  Windows so that a maze is launched in -applicationDidFinishLoading.
- Under Windows, failure to load default maze incorrectly leaves the
  application dangling with no windows on-screen.  If +nextMaze fails, it
  should try to load first-maze.  In the event that this fails, inform the user
  with a warning panel and either die, or give the user a chance to open a maze
  with the New or Open panels.
- Under Windows, PSadjustcursor() does nothing.  (click-on-crate still works,
  but the mouse simply stays in one place.) [Fixed by making native Windows
  calls.]
- Under Windows, the Scores panel has an application menu.
- Under Windows, the application icon does not display on the Info panel.
  [Fixed: Now uses "SokoSave" icon rather than "NSApplicationIcon".]
- Under Windows, creation of Library/SokoSave directory fails.  [Fixed:
  Documentation was incorrect about first object in array returned by
  NSString's -pathComponents used by mkdirs().]
- mkdirs() doesn't work for Windows Universal Naming Convention (UNC) paths.
  Unfortunately, -pathComponents returns "\" as the first component, which when
  passed to +pathWithComponents: results in a path which starts with "\" rather
  than "\\", which is no longer a UNC path.  [Fixed: Rewrote it to visit
  directories bottom-up rather than top-down.]
