Repository navigation
Conversation
The locator finds the table by ordinals 1, 2 and 3, so the row before that head, ordinal 0, was never read and every fixture storing it had no ground. Take that row when it reads as a plain row with ordinal 0, the uid doubled, a zero byte, a pitch length inside the checked range and a uid the walk did not already read; otherwise the table starts at ordinal 1 as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ground's built and rebuilt dates went through the shared game-date decode, whose 1901 floor turned every ground built in the 1800s into no date. Decode them with a stadium-local floor of 1800, below the oldest year any measured row stores, keep day 1 of 1900 as the no-date value, and leave the shared decode unchanged for other readers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stadium.all_seater_capacity holds a ground's total capacity, and the seated figure the facilities screen shows in brackets is the word returned so far only as unknown["u17"]. Add Stadium.seated_capacity for it, keep the unknown key with the same value, and correct the docstrings of the total, the expansion and capacity fields and the pitch dimensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new manager's personality stores a zero in one slot, so the marked search for name blocks never reached his block and his staff row had no name. When that search finds nothing for the human manager, his already bounded window is searched once more with personality bytes accepted from a layout floor. Only the FM27 layout sets the floor; FM26 and every other row read as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an FM26 day-one save the human manager's profile also stores a zero personality byte, so the name search skipped his block and, with no end at his closing header, took the next person's name, birth date and personality. The FM26 layout now sets the personality floor, and under the floor the human's window ends at his closing header. Only the human row changes; other staff windows are as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under the personality floor the human manager's name-block window ends at his closing header, so his contract records are now looked for up to the same header. A record with his selector in the next object is not his, and his window can no longer come out empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every club plays friendlies, so the friendly competition covers every member of a division as fully as the division does, and the lower id won the tie. A tie now goes to the competition holding the most calendar meetings between two members of the table; remaining ties keep the lower id. On five FM26 saves this changes competition_id on 11% to 31% of tables, and the rules that link to those tables follow. Over the changed tables on one save, calendar meetings between members in the voted competition rise from 226 to 12,101. The rules round-date floor is re-tuned from 0.65 to 0.87. On the four saves it applies to, linked blocks now score 0.947 to 0.987 and blocks linked to the run before them score 0.657 to 0.790; under the old vote the linked share was 0.767 to 0.901, which the new floor also fails on three of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stored count runs above goals and shots on target on some rows of every FM26 career checked, so it does not count goals everywhere. FM26 now withholds it as FM27 does: the field and CSV column stay, with None. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
experimental_span() capped frames at 8 MiB and the span at 64 MiB, so early-season FM27 saves whose span frames had outgrown 8 MiB were reported as corrupt. Default Fm27SpanLimits to the 512 MiB total and 256 MiB per-frame caps that limit_game_index already applies, from one shared definition. The integration test now passes its own small limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The span pass now defaults to 128 MiB a frame and 256 MiB in all, derived from the largest frames and spans measured on FM27 and FM26 saves with headroom, instead of reusing the 512 MiB container budget. Frames are copied into one buffer rather than kept as chunks and joined, so the pass holds the span once instead of twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FM27 Facilities screens show 0 as None, 3 and 4 as Fairly Basic and 16 as Excellent. Add a NONE member for 0 and map the other three to the existing words, so every code from 0 to 20 now has a label. Member values are unchanged. The docstrings say which codes FM27 screens named and that no FM26 screen has shown them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One FM27 club's sponsorship screen showed kind 1 as Main Kit, 3 and 23 as Stadium, 4 as General, 8 as Away Kit, 9 as Additional Kit Sponsor and 11 as Back of Shirt. The FM27 sponsor layout names those codes; every other code, and every FM26 code, stays UNKNOWN. Document that the screen's Duration is total_value / annual_value rather than the span between the dates, and that an unexpired row can sit outside the displayed sponsorship income. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adding the kind label lookup moved the cache decorator off _sponsor_row_struct, so every club rebuilt its row struct. Cache it again and test that both lookups are built once per layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A summary build number past the u32 range leaked struct.error from fmsave.open, and a leading-zero major such as 027 opened as FM27 while every reader looked up FM26 layouts, bypassing FM27's fail-closed check. Both are now skipped as non-versions, so the save is unsupported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every read reopens the file, so a save opened by a relative path raised SaveChangedError once the working folder changed. The index now keeps the absolute path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bug fixes for 0.8.1, and the version bump.
stadiums()includes the first ground in the stadium table. Refs stadiums() skips the first row of the stadium table on FM26 and FM27 #37.Stadium.seated_capacity;all_seater_capacityis documented as the total. Refs Stadium.all_seater_capacity holds the total capacity, and the seated figure is in unknown #38.goals_outside_boxis empty on FM26, as on FM27. Refs goals_outside_box exceeds goals and shots_on_target on FM26 season stats #39; follow-up in Confirm what goals_outside_box counts on FM26 #48.staff()row has their name on FM27, and the right person's details on a new FM26 career. Refs FM27: staff() returns no name for the human manager #41.Checked on FM26 and FM27 saves, against in-game screens where we have them, and with the local corpus tests.
Thanks @jeohist for the reports.
Checklist
uv run pytest,uv run ruff check .,uv run ruff format --check .anduv run pyrightpass.🤖 Generated with Claude Code