Repository navigation
Engine Mechanics
How Hearts of Iron IV loads and reads ship name list files, and how Immersive Ship Names Expanded (ISNE) is built because of it.
The facts in the first part are written by a script from the knowledge register of the author's modding tools, where each is kept once with its evidence. Each carries its id, how well it is established and the game version it was checked on. To correct one, change the register and regenerate; an edit made here is overwritten.
NAM-005 | status: vanilla | game: 1.19.3
%d. Infanterie-Division gives "3. Infanterie-Division"; %s. Divizija gives "III. Divizija". Every vanilla fallback_name holds exactly one of the two: %d in 972 division groups and 439 ship groups, %s in 66 and 91. Without a token every unit past the listed names would get the same name.
A suffix written for one language (%er) is not a token. One number token per name: the code looks for %s first and fills it, else %d. ordered texts are filled the same way [NAM-021].
Evidence: scan of both folders in game 1.19.3.0.
In a name string an apostrophe needs no escape, \" shows a double quote and \\ a backslash; any other backslash is logged and kept as it stands
NAM-008 | status: confirmed | game: 1.19.4.1
- An apostrophe needs nothing: "2. Esc O'Brien's" and the ship name "PRB PRB O'Higgins" show as written. Vanilla 1.19.4.1 has about 3,600 name strings with an apostrophe outside comments.
-
\"inside a string is a double quote in the name:"%dª Esc \"Duque de Caxias\""shows as1ª Esc "Duque de Caxias"in the division list, theªincluded. Vanilla's name files never use\". -
\\is one backslash:"%d. Esc Double\\Backslash"shows as "3. Esc Double\Backslash". - A backslash before any other character gives
Location: <file>(<line>): Backslash followed by neither quote nor backslash, if you meant to write a single backslash, write two backslashes.inerror.log, once at load. The string is still read with the backslash in it ("%d. Back\dslash"shows as "1. Back\dslash"), and the rest of the file is read normally: the next entry of the group and the next group of the file were both used.
So a validator that refuses a stray backslash is stricter than the game, which is the right side to err on. A nickname may go in \" or in single quotes; both show.
Until 2026-11-04 this record was untested and said a nickname must go in single quotes because a double quote ends the string.
Evidence: 2026-11-04, a test mod with groups whose texts held an apostrophe, \"...\", \\ and \d (the last in a file of its own, zz_probe_9_backslash.txt, with a second group after it). The \d line was the only log line for those files. The names were read in the division overview after loading a save whose divisions used those groups (captures b-overview-* in hoi4-1440p-ui-mod/scratch/checks; Artifacts/namelists/division-names-escapes.png).
A ship name group holds name, for_countries, can_use, type = ship, ship_types, prefix, fallback_name and unique or ordered
NAM-009 | status: vanilla | game: 1.19.3
TAG_DD_HISTORICAL =
{
name = NAME_THEME_HISTORICAL_DESTROYERS
for_countries = { TAG }
can_use = { always = yes }
type = ship
ship_types = { ship_hull_light destroyer }
prefix = "HMS "
fallback_name = "Destroyer %d"
unique = { "Ship One" "Ship Two" }
}
name is a text or a localisation key. unique is a pool of names; ordered gives names by number. Vanilla 1.19.3 has 620 groups with type = ship.
A group without ship_types is offered for every hull in the ship designer; the code matches it to every variant [NAM-025]. Immersive Ship Names Expanded builds its thematic pools on that.
Evidence: scan of common/units/names_ships/ in game 1.19.3.0. A launch of 2026-10-20 (the owner's, with RT56 - VNR - UTT Standalone v14 and VNR - Ship Names Merge): a German destroyer variant offered all 23 groups of Germany's file that have no ship_types or list ship_hull_light, and the fascism-gated GER_NSDAP, while Germany was fascist. Among them were 15 groups the merge adds with no ship_types, and VNR's 7 theme groups without one. Until then the every-hull reading rested on the code and on vanilla's use.
NAM-010 | status: vanilla | game: 1.19.4.1
Hull archetypes: ship_hull_light, ship_hull_cruiser, ship_hull_heavy, ship_hull_carrier, ship_hull_submarine, ship_hull_cruiser_submarine, ship_hull_midget_submarine. Sub-units: destroyer, light_cruiser, heavy_cruiser, battle_cruiser, battleship, carrier, submarine.
A group usually names the hull and the sub-unit together (ship_types = { ship_hull_light destroyer }).
screen_ship and capital_ship are categories used in script, not tokens of this list: vanilla does not use them here, and the rules of Immersive Ship Names Expanded forbid them. An older page of that mod's wiki listed them.
A role cannot be picked out with ship_types: a minelayer is a light hull and an escort carrier a carrier hull. A pool for a role therefore carries no ship_types.
Evidence: scan of common/units/names_ships/ in game 1.19.3.0. A scan of 1.19.4.1 on 2026-11-04 found the same fourteen tokens; screen_ship and capital_ship occur only in commented-out lines of GER_ship_name.txt.
NAM-011 | status: confirmed | game: 1.19.4.1
prefix = "NRB" gives "NRBMinas Gerais". Of the 498 prefixes in vanilla 1.19.3, 488 end with a space and 10 are empty; 1.19.4.1 has the same counts.
The code does the same: after each definition of a group it writes the prefix and the name together, with nothing between, into every ordered and unique name the group holds [NAM-020]. Division groups may carry a prefix too [NAM-025].
Evidence: the joined name is reported in the rules of Immersive Ship Names Expanded without a dated test; the count is from common/units/names_ships/ in game 1.19.3.0 and 1.19.4.1 (comment lines left out); the code is FUN_1409c54f0 of hoi4.exe 1.19.3. In the game on 2026-11-04 (1.19.4.1): a production line of a destroyer variant whose group had prefix = "PRB" and unique = { "Nospace" } reserved the name "PRBNospace" (Artifacts/namelists/ship-prefix-reserved-names.png).
A ship name group defined again in a file with another name is added to the first instead of replacing it
NAM-012 | status: confirmed | game: 1.19.4.1
Reported by Immersive Ship Names Expanded: with a file ISNE_<TAG>_ship_names.txt beside the vanilla <TAG>_ship_names.txt, the properties of a group both define pile up. The prefix shows twice ("NRB NRB ", "BACH BACH "), and the mod's names are appended after vanilla's.
That mod therefore ships its files under the vanilla file names, so that each replaces the vanilla file whole [LOAD-010].
The read of hoi4.exe 1.19.3 explains both effects: a second definition of a tag is read into the group already loaded, its unique names are added after the first's, and the prefix is written again in front of every stored name, so the names of the first definition carry it twice [NAM-020]. Both folders go through the same loader and parser, and division groups merge the same way [NAM-002].
Evidence: the doubled prefixes are quoted in the mod's rules; no dated launch is recorded. The code read of 2026-10-15 [NAM-020]. A launch of 2026-10-15 at 22:57 with only a test mod loaded, whose common/units/names_ships/zz_exe_check_ships.txt defined GER_DD_HISTORICAL = { name = "EXE SHIP GROUP" prefix = "EXE " unique = { "Exe-Testschiff" } }: Germany's destroyer production lines showed EXE Exe-Testschiff, EXE Z1 Leberecht Maass, EXE Z2 Georg Thiele, EXE Z3 Max Schultz and EXE Z4 Richard Beitzen. The vanilla names stayed and took the new prefix, and the new name was added; vanilla's group has no prefix, so a doubled one could not show. A launch of 2026-11-04 at 11:58 (game 1.19.4.1) with a test mod whose zz_probe_1_ships.txt defined PRB_PFX_SP with prefix = "PRB " and unique = { "Alpha" "Bravo" "Charlie" "O'Higgins" } and whose zz_probe_2_ships_merge.txt defined it again with the same prefix and unique = { "Echo" }: the variant's production lines reserved "PRB PRB Alpha", "PRB PRB Bravo", "PRB PRB Charlie" and "PRB PRB O'Higgins", the doubled prefix seen in the game (Artifacts/namelists/ship-prefix-reserved-names.png).
The name list selector of the ship designer cuts a group name longer than about 20 to 22 characters with an ellipsis
NAM-013 | status: confirmed | game: 1.19.4.1
The cut goes by the width of the text, not by a number of characters. In the vanilla ship designer:
- the box at the top shows the chosen group as "Historical Battles.." for vanilla's 22-character "Historical Battleships", and its tooltip gives the whole name;
- the opened list shows "Historical Battleships" whole, but cuts "Admirals & Commissioners" (24 characters) to "Admirals & Commiss...", "Historical Battlecruisers" to "Historical Battlecru..." and a ruler of digits after 16 characters.
A group name therefore shows whole only up to about 20 characters of ordinary text. The list is already filtered to the player's country, so the country's name can be left out.
Until 2026-11-04 this record said the cut came after about 30 characters, as the wiki of Immersive Ship Names Expanded stated; the game showed otherwise.
Evidence: 2026-11-04, a test mod with Germany's ship groups PRB_ST24 "Admirals & Commissioners", PRB_ST37 "Admirals & Commissioners of the Fleet" and rulers of 24, 30, 34 and 40 characters (R30-5678...), seen in the ship designer of the Scharnhorst class (Artifacts/namelists/ship-selector-vanilla.png).
Vanilla script assigns ship name groups by tag, so a file that replaces the vanilla one must keep every such tag
NAM-017 | status: vanilla | game: 1.19.4.1
Starting ship variants in history/countries/, and focus and decision rewards, pick a name list with name_group = <group tag>. A mod file under the vanilla file's name takes the place of the whole file [LOAD-010], so a tag it leaves out no longer exists for that script.
Immersive Ship Names Expanded keeps every tag vanilla assigns, also where the group becomes a pool for a role (USA_CVL_HISTORICAL, USA_MINELAYERS_HISTORICAL). Its build.ps1 -InspectVanilla <TAG> lists the tags, and -Audit fails on a missing one.
A tag the replacing file leaves out that no vanilla script assigns is still lost to saves made without the mod: loading such a save logs it and drops it [NAM-019].
Evidence: the name_group uses in the vanilla files, as listed by that mod's tooling. On 2026-11-04 build.ps1 -Audit <TAG> of Immersive Ship Names Expanded passed for all 22 of its nations against the 1.19.4.1 install, with no missing tag.
A save keeps each country's name group lists: a tag no loaded file defines is dropped on load, and a group a mod adds later is offered only once its tag is written into the save
NAM-019 | status: confirmed | game: 1.19.4.1
A save stores, per country, the name group tags it may use in two lists inside one block, for divisions (division_names_tracker) and for ships (ship_names_tracker), each with unavailable_groups and available_groups [NAM-023]. In a binary (HOI4bin) save the ship block is <0x3c85> = { <0x390b> = { locked tags } <0x390a> = { available tags } ... }, and each tag is stored as 0F 00 <len16> <ascii>. The format has no offsets or checksum, so tags can be appended to the available list and the game reads the edited file.
On load, a tag that no loaded file defines gets this line in error.log, and the group is not offered:
Error reading division names tracker - unknown division names group tag: USA_DD_HISTORICAL: }, near line: 0
A group that a mod added after the game was started is not put into the lists, neither on load nor by the country's update in the eight game days watched, and is not offered. A mod that adds name groups therefore shows them only in a new game, while its texts for groups the save already holds show at once (the names are worked out again from the group [NAM-021]). The reverse holds as well: a mod that replaces a vanilla file without some of its tags [LOAD-010] costs a save made without the mod those groups [NAM-017].
A tag appended by hand to a copy of the save, when a loaded file defines it, is kept, and the country's update sorts it into available or unavailable by its can_use [NAM-018], like a group the save always had (seen within two game days; the hourly update suggests sooner). The game's load list then marks the edited text save with Savegame is truncated, the game will likely crash! in error.log; it loaded and ran.
When groups are missing in game, first check which mod's file is in effect (see [LOAD-014]) before editing the save.
Evidence: 2026-11-04, game 1.19.4.1. A text save made with only a test mod (nlp_base, Germany, 5 January 1936) was loaded with Immersive Namelists Expanded, Immersive Ship Names Expanded and the test mod: error.log named nine vanilla ship tags that the second mod's files leave out (USA_DE_HISTORICAL, USA_CLAA_HISTORICAL, AUS_HRE_EMPERORS, AUS_PZ_HISTORICAL, ITA_MINELAYERS_HISTORICAL, ROM_REGIONS, YUG_REGIONS, MEX_AZTECS, MEX_LEADERS). Saves made at once and eight game days later held none of the 58 German division groups only the first mod defines, nor any of the 19 USA and 56 ENG ship groups only the second defines, and Germany's division designer offered none of them. A copy with the 58 and the 19 tags appended to the two available_groups lists, loaded from the game menu, kept all of them; in a save 43 game hours later Germany's list stood at 74 available and 21 unavailable (87 and 8 as written), USA's at 33 and 8. Earlier evidence: ISNE, 2026-10-15. Loading USA.hoi4 with 20 tags appended logged the line above for each of them, because VNR's USA_ship_names.txt was in effect instead of ISNE's (no USA ship in three saves of the campaign carried an ISNE-only name). An earlier save of the same campaign still held the appended tags six game years later, so not every load drops them; why is not known. Until 2026-10-15 this record said the game accepts appended tags and re-gates them. That was inferred from that save and is withdrawn.
A name group defined again in a later file is read into the group already loaded: lists and new ordered numbers are added, single values replaced, and the prefix is applied again
NAM-020 | status: confirmed | game: 1.19.4.1
The four folders common/units/names_divisions/, names_ships/, codenames_operatives/ and names_railway_guns/ load into one database through one parser, in that order, each folder's .txt files in byte order of their names [LOAD-013] and without subfolders. What kind of group a block is comes from its type key (army when it has none), not from the folder [NAM-025].
When a file defines a tag that an earlier file already defined as an army, ship or codename group (the tags compared without regard to case), the game reads the new block into the group it already has. Nothing is cleared first. Per key:
| Key | Second definition |
|---|---|
name, fallback_name, prefix, type, can_use
|
replaced |
for_countries, division_types
|
added; a value already there is skipped |
link_numbering_with, ship_types
|
added |
ordered |
a number the group has gets the new text in its old place, with the log line Adding a duplicate ORDERED division name (...); a new number is added after the others |
unique, unordered
|
added after the names already there |
After each block the group puts its prefix in front of every ordered and unique name it holds [NAM-011]. A group read twice therefore carries the prefix twice on the names of its first definition, and the second definition's names stand after the first's: the "NRB NRB " and appended names reported in [NAM-012].
The belief that the later group replaces the earlier one whole, with the left-out vanilla ordered keys gone, is disproved [NAM-002]. A mod that redefines a vanilla tag still gets the names it writes for the numbers it writes, keeps vanilla's other numbers, and doubles unique names and the prefix; a ship name mod avoids that by shipping the vanilla file under its own name [LOAD-010].
Evidence: a launch of 2026-10-15 at 22:57 with only a test mod loaded: its zz_ file's GER_Inf_01 replaced the group's name and the text of number 1, with the log line above, and kept vanilla's numbers 2 to 15 and its fallback name [NAM-002]; its zz_ file's GER_DD_HISTORICAL put its prefix in front of the vanilla names and added its unique name [NAM-012]. The designer shows only the first 15 names of a group, so where the new number 990 stood was not seen. A launch of 2026-10-15 at 23:40 with Immersive Namelists Expanded, whose zz_INEX_* files redefine vanilla groups: error.log had 3,415 Adding a duplicate ORDERED division name lines naming those files (783 of them zz_INEX_GER_names_divisions.txt), and Germany's "Special Infantry Divisions" (GER_ALTINF_01) showed as its first names 22, 78, 44, 91, 19, 20, "560. Volks-Grenadier-Division", 12, 361, 18, 276, 277, 326, 708 and 349: vanilla's list in vanilla's order with the mod's texts in place of the numbers the mod writes, and vanilla's Volks-Grenadier names that the mod leaves out still there. The code: hoi4.exe 1.19.3 read in Ghidra on 2026-10-15. FUN_14018bb20 loads the four folders into the database FUN_140162d30 returns; FUN_140192fc0 and FUN_1409c5360 list each folder with .txt and depth 0 (FUN_1424dc380). FUN_1409c11c0 takes each top-level tag, looks it up with _stricmp in the army, ship and codename maps and, when found, calls the existing CNameGroup's read again; otherwise it creates one. That read is FUN_1424be690 (CPersistent::Read, persistent.cpp): the slot it calls first, 0x30 of CNameGroup::vftable, is an empty function (ret), and after the closing brace it calls slot 0x40, FUN_1409c54f0, which writes the prefix in front of each stored name. The keys are FUN_1409c6870, matched to token ids with DumpTokenTable.java. Seen again on 1.19.4.1 on 2026-11-04: a test group PRB_ORD with ordered 5 and 10 in zz_probe_1_main.txt, defined again in zz_probe_2_merge.txt with a new name, a new number 2 and number 5 once more, showed the new name in the designer, logged Adding a duplicate ORDERED division name (common/units/names_divisions/zz_probe_2_merge.txt:5) id: Probe Ordered Merged id:5 name:%d. Ord Five Again, and gave four new divisions the numbers 5, 10, 2 and 11: the added number after the old ones, then the fallback. The ship side showed the doubled prefix [NAM-012]. With Immersive Namelists Expanded the log had 6,444 duplicate lines naming its files (and one from the test mod). A count of the ordered numbers in its files that vanilla's groups of the same tags already have, made with a PowerShell scan that read each top-level block's ordered keys, gave 6,450; the small gap was not traced. The rise from 3,415 follows the mod's growth (23 commits to its common/ since 2026-10-15), not a change of the engine.
A ship's name is reserved by its production line: the first free unique name of the variant's group in file order, then the first free ordered entry, then the fallback
NAM-022 | status: confirmed | game: 1.19.4.1
The naval production line names the ships it is going to build when it reserves them, from the name group of the equipment variant, in this order:
- The first
uniquename of the group that is free, in the order of the file; not a random one. - If none is free, the first free
orderedentry, and after those the number one above the highest in use, throughfallback_name, as for divisions [NAM-021].
A name is taken while a ship of the country has it, a production line has reserved it, or a sunk ship of the country had it [NAM-023]. The prefix is already part of each stored name [NAM-011]. Railway guns are named the same way from their own groups.
A ship deployed from the line takes the name the line reserved for it. A ship created without one, and without a name from the effect that creates it, goes straight to step 2; so do the ships renamed when their group is lost [NAM-024].
Evidence: a launch of 2026-10-15 at 22:57 with a test mod that added the unique name Exe-Testschiff to GER_DD_HISTORICAL, whose vanilla names are all ordered [NAM-012]: of Germany's 1936 destroyer lines, the first (line 13, a 1934 class destroyer) showed EXE Exe-Testschiff, line 14 EXE Z1 Leberecht Maass and lines 19 to 21 EXE Z2 Georg Thiele to EXE Z4 Richard Beitzen. The fallback was not reached. The code: hoi4.exe 1.19.3 read in Ghidra on 2026-10-15. FUN_14145b400, called from the production code (FUN_140f6ede0, FUN_140f6f630 and others; production.cpp), builds a name record for the equipment and calls FUN_1409c0850(tracker, name, 0), then (..., 1), with the ship tracker of the country for naval equipment and the railway gun tracker otherwise. FUN_140d0f5a0 (CNavalDeployment::CreateUnit, deployment.cpp) gives the new ship the line's reserved name, else an explicit name, else FUN_140c349d0, which calls the ordered branch first. The markers FUN_1409beec0 and FUN_1409beb60 go through the ships of the country's navies, the names reserved by its production lines and the records of sunk ships. Seen on 1.19.4.1 on 2026-11-04: four production lines that a scripted add_equipment_production made for a variant of the group PRB_PFX_SP (unique = { "Alpha" "Bravo" "Charlie" "O'Higgins" }) reserved "PRB PRB Alpha", "PRB PRB Bravo", "PRB PRB Charlie" and "PRB PRB O'Higgins" in that order [NAM-012]; ships of the same variant made by create_ship without a name took the fallback "Probe DD 1", "Probe DD 2" and "Probe DD 3" (ship_name={ type=1 name_order=<n> } in the save, against override="<name>" for the history's ships), none of the unique names. add_equipment_production lines are not found in a text save by the variant's id; the production screen shows them.
A destroyed division's name stays taken until its template is deleted; a ship removed by destroy_ships frees its number at once
NAM-023 | status: confirmed | game: 1.19.4.1
When a unit is deleted, the game keeps a record of its name in its country's tracker, and every name pick counts the records as taken [NAM-021] [NAM-022]:
-
Division. The country's regular update drops a record once the template the division belonged to no longer exists. The number of a destroyed "5. Infanterie-Division" is therefore free again only after its template is deleted; while the template exists, new divisions get other numbers. Divisions removed by the effect
delete_unitcount the same way as destroyed ones. A new template of the same name and group made right afterdelete_unit_template_and_unitsgives its first division the first number again, in the same game hour. -
Ship. As far as the code was read, the regular update leaves a ship's record in place, so a sunk ship's
uniquename ororderednumber would not be given again; a reset that empties a whole tracker exists (FUN_1409c19c0), and when it runs was not read. In the game a ship removed with the effectdestroy_shipsleft no such hold: the next ship of its variant got its fallback number again. Whether a ship sunk in combat, or one with auniquename, keeps its name taken has not been seen. Until 2026-11-04 this record said a sunk ship's name stays taken, from the code alone. - Railway gun. Records are kept in a tracker of their own and are left in place too.
A record of a unit whose name the player typed blocks nothing.
The trackers are saved with the game (keys such as division_names_tracker and ship_names_tracker, each with available_groups, unavailable_groups and one post_mortem block per record; the ship tracker in a binary save: [NAM-019]). On loading, a group tag that no longer exists gives Error reading division names tracker - unknown division names group tag: <tag> in error.log, once per tag.
Evidence: a launch of 2026-10-15 at 23:55 with vanilla names, Germany: "5. Infanterie-Division" was brought to no strength with the console's damage_units 10000 and was gone from its army an hour later. A new division of the same template then got "26. Infanterie-Division", and the list of historical names still available on its deployment line had no "5. Infanterie-Division" [NAM-021]. Not seen in that launch: the number coming free once the template is deleted (seen on 2026-11-04, below), and the ship and railway gun records. The code: hoi4.exe 1.19.3 read in Ghidra on 2026-10-15. FUN_1401d6690 (DeleteUnit, gamestate.cpp) calls FUN_140c8e520 for a division, FUN_140c3d900 for a ship and FUN_140e8d310 for a railway gun, each appending a record with FUN_1409c0fa0. FUN_1409c1780, called from the country update FUN_1406e8210 for every tracker, removes army records whose template is gone and leaves the others. FUN_1409c8330 and FUN_1409c8510 read the saved tracker (token ids available_groups 0x390a, unavailable_groups 0x390b, post_mortem 0x390c). The loading message was in the user's error.log of the launch of 2026-10-15 at 21:53, one line per USA_* ship group, on loading save games/USA.hoi4 (game 1.19.3; the next launch overwrote the log). On 1.19.4.1 on 2026-11-04, read from text saves: four divisions of the template "Probe Seq" held the fallback numbers 1 to 4; after delete_unit = { division_template = "Probe Seq" } and two game hours, the next division of that template got 5. After delete_unit_template_and_units and a new "Probe Seq" template with the same group, made in the same game hour, its division got 1. A create_ship ship of a test variant held fallback number 1 (ship id 2717); after destroy_ships = { type = destroyer count = all }, create_ship made ship 2737 with number 1, and two more create_ship calls got 2 and 3, so the numbers do count up.
When a ship name group stops passing can_use, the variants that use it lose it and their ships are renamed
NAM-024 | status: confirmed | game: 1.19.4.1
The country's update checks can_use of its name groups again [NAM-018]. When that changes which ship groups are available, the game:
- takes the group away from every equipment variant of the country whose group is no longer available;
- names each ship of such a variant in the country's navies again; with no group the name comes from the country's names in
common/units/names/[NAM-014] and stays fixed; - drops the names the production lines of those variants had reserved, so their next ships are named the same way.
The ships are not given their old names back when the group becomes available again; the variant has no group until one is chosen for it again.
A ship name group with a can_use that a country can lose during the game (a government, an idea) therefore renames that country's ships when it is lost. Gating a ship group only by DLC or by always = yes avoids it. Division templates are treated differently: they get another group [NAM-018].
Only a change of the lists starts this. A variant that script gave a group while the group was not in the available list (made and switched back within the same game hour, before the update had put it there) keeps the group and its ship its name when the trigger fails, since the list does not change.
Evidence: two launches of 2026-10-15 with a test mod that redefined a vanilla ship group's can_use as NOT = { has_country_flag = <flag> } [NAM-020]. At 23:40, with GER_DD_HISTORICAL: two hours after the flag was set, Germany's five destroyer production lines had dropped their reserved names ("Z1 Leberecht Maass" to "Z5 Paul Jacobi") and showed the button's fallback label "Ship Names". At 23:55, with GER_TB_HISTORICAL: one hour after the flag was set, the four Type 24 boats of the Hochseeflotte, "Jaguar", "Leopard", "Luchs" and "Tiger", were "Z1" to "Z4", the first names of the destroyer list of common/units/names/00_GER_names.txt; two hours after the flag was cleared they were still "Z1" to "Z4". The code: hoi4.exe 1.19.3 read in Ghidra on 2026-10-15. FUN_1406fdba0 runs the can_use update FUN_1409c9680 (profiler label division_names.update) on the ship tracker and, when it reports a change (FUN_1409c6520), calls FUN_140717700. That function clears the group field (+0x480) of each variant whose group is not in the tracker's available list, calls FUN_140c349d0 on each ship of those variants, and clears the reserved names of the naval production lines building them. FUN_140c349d0 then finds no group (FUN_1409c40e0 reads the variant's field) and falls through to FUN_1409c1b30, the common/units/names naming. Seen again on 1.19.4.1 on 2026-11-04 with a test group PRB_SGATE (can_use = { has_government = communism }) on the variant "Probe DDG", made by script while Germany was communist, with one ship and one production line: switched back to fascism in the same game hour, the variant kept the group through two game days; communist for 17 game hours and then fascist for 20, the saved variant had no division_names_group, its ship had override="Z1" override_set_programmatically=yes, and its production line showed "Ship Names".
Division, ship, railway gun and codename groups share one parser: every group may use every key, including unordered
NAM-025 | status: confirmed | game: 1.19.3
One parser reads the groups of all four folders [NAM-020], so every group may use every key:
| Key | Read as | A bad value |
|---|---|---|
name |
text or localisation key for the selector | |
type |
army (also when the key is missing), ship, railway_gun, codename
|
unexpected name type |
for_countries |
country tags; empty or missing means every country | ... There is unknown tag in for_countries scope: <tag> |
can_use |
trigger; empty or only always = yes makes the group always available, any other is checked in the country's update [NAM-018] |
|
division_types |
sub-unit ids; support companies (artillery, anti_air, military_police) are sub-unit ids too |
..., division_types has unknown token: <token> |
link_numbering_with |
tags of army, ship or codename groups; resolved after all files, both ways [NAM-021]; the group's own tag is skipped silently | Error loading division names group. Unknown tag specified in link_numbering_with scope: <tag> |
fallback_name |
text with %d or %s [NAM-005] |
|
prefix |
text put in front of every ordered and unique name [NAM-011] |
|
ship_types |
script keywords, looked up without regard to case; empty means every variant [NAM-009] | none: an unknown word is kept as a placeholder and matches nothing |
ordered |
<number> = { "name" "description" "link" } |
Error reading ORDERED division names group - <name>. The entry index of <n> is out of range. Should be a value of 1 or higher.; a number used twice: [NAM-020] |
unique |
names | |
unordered |
<any key> = { "name" "description" "link" }; the key is not read; added to the same list as unique
|
a bare string in the list (unordered = { "Name" }): Error: unexpected token in file: "<file>" near line: <n> ( Name ), and the group's keys after the list, ordered included, are lost |
| any other key | Unexpected token while reading division names database: <key> |
The messages starting Error reading and Unexpected token are parser errors with the file and line; <name> in them is the group's name, not its tag.
So a division group may carry unique, unordered and prefix, and a ship group ordered (eight vanilla ship files do). unordered is described in the header comment of every vanilla division file ("a list of historical division names that did not have a number ... available here mainly for a mods"), with the description shown where a name is chosen and the link "not currently working"; no vanilla file uses it. New divisions take these names before the ordered ones [NAM-021].
An ordered number below 1 (0, -1) also throws the reader off by one level of braces: the entries after it are not read, and what follows in the group is read as blocks of their own. Each <key> = { ... } after it (2 = { ... }, unique = { ... }) becomes a name group without a name and without for_countries, which every country is offered as an empty entry in the division designer; its texts give Unexpected token while reading division names database: : <text>, and a key after the ordered block can end in such a group (one of them carried the broken group's fallback_name). A group whose ordered block ends with the bad number loses the keys after the block, and the file ends with an extra } ([parser.cpp:1111]: Error: unexpected token in file: ... ( } )). The next group of the same file is read normally. A group with a wrong type is still read, as an army group. Keep every ordered number at 1 or higher.
can_use = { always = no } is evaluated like any other trigger: the group is never available, and a template that names it is moved [NAM-018].
A key written twice in one block gives no message: both are read, so lists add up and the last single value stays. The folders are listed without subfolders: a file in a subfolder of common/units/names_divisions/ is not read.
Evidence: a launch of 2026-10-15 at 22:57 with only a test mod loaded. Its division group EXE_PARSE (for_countries = { GER XQZ }, division_types = { "artillery" "no_such_unit" }, link_numbering_with = { EXE_PARSE XQZ_NO_SUCH_GROUP }, unordered, unique, ordered with the numbers 0, 5 and 5, then bogus_key = yes) gave in error.log, with the file and line: Error reading division names group - EXE PARSE GROUP. There is unknown tag in for_countries scope: XQZ, Error reading division names group - EXE PARSE GROUP, division_types has unknown token: no_such_unit, Error reading ORDERED division names group - EXE PARSE GROUP. The entry index of 0 is out of range. Should be a value of 1 or higher., Unexpected token while reading division names database: : %d. EXE-Five and ... : 5, and [divisionnamesdatabase.cpp:1684]: Error loading division names group. Unknown tag specified in link_numbering_with scope: XQZ_NO_SUCH_GROUP; nothing for the group's own tag or for bogus_key. The group EXE_PARSE_TYPE with type = boat gave unexpected name type: type. The division designer offered both groups to Germany; "EXE PARSE GROUP" showed the example names EXE Unordered One and EXE Unique One and no ordered one. The designer's list also had two entries without a name, the groups made of the misread keys. A launch of 2026-10-15 at 23:40 with one broken group per file: 0 as the last entry (zz_exe2_zero_last.txt), 0 first with a unique list and a fallback_name after the block, followed by a second group (zz_exe2_zero_first.txt), and -1 first (zz_exe2_negative.txt), gave the lines above and five empty entries, one of them with the example "Fallback name: 123. ZF-Fallback"; "EXE AFTER ZERO", the second group of its file, was listed with its name. bogus_key = yes alone in a group gave Unexpected token while reading division names database: : bogus_key, and a number written twice in one block gave Adding a duplicate ORDERED division name (common/units/names_divisions/zz_exe2_dup.txt:6) id: EXE DUP id:5 name:%d. D-Five-Again. A launch of 2026-11-04 at 11:58 (game 1.19.4.1) with two test groups that had unordered = { "Seq Unordered" } and unordered = { "Hobart's Funnies" "..." } before their ordered blocks: Error: unexpected token in file: "common/units/names_divisions/zz_probe_1_main.txt" near line: 71 ( Seq Unordered ) and ... line: 83 ( Hobart's Funnies ), and the divisions of both groups got only fallback names; with keyed entries (u1 = { "..." }) the next launch read the same groups without a message and showed their ordered texts. The code: hoi4.exe 1.19.3 read in Ghidra on 2026-10-15. FUN_1409c6870 reads the keys by token id, matched with DumpTokenTable.java: name 0x1b, type 0xe1 (values army 0x289d, ship 0x28a0, codename 0x3d17, railway_gun 0x4d14), prefix 0x30f8, unique 0x30fa, can_use 0x37ae, division_types 0x38dd, link_numbering_with 0x38de, fallback_name 0x38e0, for_countries 0x38e4, ordered 0x3907, unordered 0x3908, ship_types 0x3c84. FUN_1409c54f0 sets the always-available flag (no trigger, or one always, token 0x2bef), FUN_1409c59a0 tests for_countries, FUN_1409c6530 resolves the links, FUN_1409c1610 matches ship_types against the variant (no entry: every variant). The repeated-key check of CPersistent::Read, slot 0x28 of CNameGroup::vftable, is a function returning false. FUN_1409c5360 lists each folder with depth 0.
Most vanilla bitmap fonts have the cedilla letters ş and ţ but not the Romanian comma-below letters ș and ț
SPR-023 | status: vanilla | game: 1.19.3
A vanilla bitmap font (gfx/fonts/*.fnt) lists each glyph it has as a line char id=<decimal code point> . Of the 59 .fnt files in 1.19.3:
- The comma-below letters of standard Romanian,
Ș ș Ț ț(U+0218 to U+021B, ids 536 to 539), are in only five:hoi_20,hoi_22tech,hoi_30,hoi_33andhoi_arrow_font. The map fonthoi_mapfont4,hoi_16mbs,hoi_18mbs,hoi_20b,hoi_20bs,hoi_26mbs, the typewriter fonts andhoi_16tooltip3lack them. - The cedilla letters
Ş ş Ţ ţ(U+015E to U+0163) andĂ ă(U+0102, U+0103) are in every Latin UI font named above, the map font included. -
hoi_24headerandhoi_36headerlackă,ţ,čandğ(ids 259, 355, 269, 287) but haveş,ł,âandî. - The Cyrillic fonts,
garamond_12andnewsfeed_bodylack all of these letters. - No vanilla file under
common/,history/orlocalisation/english/contains a comma-below letter.
What to do: write Romanian text that the map or the fleet views show (ship names, state and city names) with the cedilla forms ş and ţ. How the game draws a code point that the font lacks (blank, box or skipped) was not checked in game.
Evidence: grep "char id=<id> " over C:\Gaming\Steam\steamapps\common\Hearts of Iron IV\gfx\fonts\*.fnt and a byte search for C8 98 to C8 9B over common/, history/ and localisation/english/, game 1.19.3, 2026-10-11 and 2026-10-14.
-
File names:
common/units/names_ships/<TAG>_ship_names.txt, the vanilla path and file name, so that each ISNE file takes the place of the vanilla file. - Vanilla tags: every group tag that vanilla script assigns stays defined under its vanilla tag.
- One national prefix, ending with a space, in every group, unless the nation deliberately has none.
- Conditions: a group is gated by government or by tag, never by a focus, a decision or a flag.
-
No empty
uniqueororderedblock. This is a rule of the mod.
ISNE namelists are structured into two broad functional categories:
These namelists are bound to specific hull tokens using ship_types = { ... }. They represent the official naval naming doctrine for specific ship classes:
-
Destroyers & Escorts (
DD): Fast craft, torpedo boats, martial descriptors, virtues. -
Submarines (
SS): Aquatic animals, sea beasts, mythological water spirits. -
Light Cruisers (
CL): Major coastal cities, ports, trade hubs. -
Heavy Cruisers / Coastal Defense (
CA): Cultural heroes, epic mythology, national figures. -
Battleships & Capital Ships (
BB): Historical provinces, regions, legendary monarchs. -
Carriers (
CV): Sky deities, weather phenomena, raptors/birds of prey.
FIN_DD_HISTORICAL = {
name = NAME_THEME_HISTORICAL_DESTROYERS
for_countries = { FIN }
type = ship
ship_types = { ship_hull_light destroyer }
fallback_name = "Hävittäjä %d"
unique = { "Sisu" "Hurja" "Myrsky" }
}
Topic namelists provide expansive pools centered around a specific concept (e.g. Birds, Fish, Legendary Rulers, Cities, Provinces, Rivers). By omitting the ship_types restriction (or specifying all naval hull types), these namelists become selectable in the Ship Designer for any hull type, allowing players to name flotillas or specialized squadrons according to their roleplay preferences:
FIN_BIRDS = {
name = "Birds"
for_countries = { FIN }
type = ship
# Omitting ship_types allows this namelist to be assigned to any hull class
fallback_name = "Lintu %d"
unique = { "Kotka" "Haukka" "Korpisoturi" "Tuulihaukka" "Merikotka" "Sääksi" }
}
To ensure that wartime fleets do not exhaust names during extended naval campaigns:
-
Destroyers & Escorts (
DD): 100–140+ unique names (minimum 80+ for minor navies). -
Submarines (
SS): 60–80+ unique names (minimum 50+ for minor navies). -
Light Cruisers (
CL): 50–70+ unique names (minimum 40–45+ for minor navies). -
Heavy Cruisers & Coastal Defense (
CA): 35–45+ unique names. -
Capital Ships (
BB/BC): 30–45+ unique names. -
Aircraft Carriers (
CV): 30–40+ unique names. - Universal Thematic Pools: 35–60+ unique names per pool where thematic scope permits.