Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2021-08-16 01:58:47 +03:00
699 changed files with 41188 additions and 22618 deletions

View File

@@ -0,0 +1,557 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<!-- derived from Iron Horse branch at https://github.com/andythenorth/iron-horse/tree/company_colour_indexes -->
<head>
<title>OpenTTD Company Colour Indexes</title>
<meta charset="UTF-8" />
<style>
th {
vertical-align: top;
border-bottom: solid 1px #ddd;
padding-top: 30px;
padding-right: 15px;
text-align: right;
}
tr.top td {
padding-top: 20px;
}
tr.bottom td {
border-bottom: solid 1px #ddd;
padding-bottom: 10px;
}
td {
text-align: right;
}
span {
border: solid 1px #000;
width: 32px;
height: 32px;
display: inline-block;
vertical-align: top;
margin-left: 0px;
}
</style>
</head>
<body>
<h1>Company Colour Indexes</h1>
<p>Hex / dec indexes into the DOS palette</p>
<p>
Visual representation of values derived from <a href="https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186">https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186</a>
</p>
<table style="margin-top:30px;">
<tbody>
<tr class="top">
<th rowspan="3">COLOUR_DARK_BLUE</th>
<td><span style="background-color:rgb( 8, 24, 88)"></span></td>
<td><span style="background-color:rgb( 12, 36, 104)"></span></td>
<td><span style="background-color:rgb( 20, 52, 124)"></span></td>
<td><span style="background-color:rgb( 28, 68, 140)"></span></td>
<td><span style="background-color:rgb( 40, 92, 164)"></span></td>
<td><span style="background-color:rgb( 56, 120, 188)"></span></td>
<td><span style="background-color:rgb( 72, 152, 216)"></span></td>
<td><span style="background-color:rgb(100, 172, 224)"></span></td>
</tr>
<tr>
<td>0xc6</td>
<td>0xc7</td>
<td>0xc8</td>
<td>0xc9</td>
<td>0xca</td>
<td>0xcb</td>
<td>0xcc</td>
<td>0xcd</td>
</tr>
<tr class="bottom">
<td>198</td>
<td>199</td>
<td>200</td>
<td>201</td>
<td>202</td>
<td>203</td>
<td>204</td>
<td>205</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PALE_GREEN</th>
<td><span style="background-color:rgb( 16, 52, 24)"></span></td>
<td><span style="background-color:rgb( 32, 72, 44)"></span></td>
<td><span style="background-color:rgb( 56, 96, 72)"></span></td>
<td><span style="background-color:rgb( 76, 116, 88)"></span></td>
<td><span style="background-color:rgb( 96, 136, 108)"></span></td>
<td><span style="background-color:rgb(120, 164, 136)"></span></td>
<td><span style="background-color:rgb(152, 192, 168)"></span></td>
<td><span style="background-color:rgb(184, 220, 200)"></span></td>
</tr>
<tr>
<td>0x60</td>
<td>0x61</td>
<td>0x62</td>
<td>0x63</td>
<td>0x64</td>
<td>0x65</td>
<td>0x66</td>
<td>0x67</td>
</tr>
<tr class="bottom">
<td>96</td>
<td>97</td>
<td>98</td>
<td>99</td>
<td>100</td>
<td>101</td>
<td>102</td>
<td>103</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PINK</th>
<td><span style="background-color:rgb(112, 16, 32)"></span></td>
<td><span style="background-color:rgb(136, 32, 52)"></span></td>
<td><span style="background-color:rgb(160, 56, 76)"></span></td>
<td><span style="background-color:rgb(188, 84, 108)"></span></td>
<td><span style="background-color:rgb(204, 104, 124)"></span></td>
<td><span style="background-color:rgb(220, 132, 144)"></span></td>
<td><span style="background-color:rgb(236, 156, 164)"></span></td>
<td><span style="background-color:rgb(252, 188, 192)"></span></td>
</tr>
<tr>
<td>0x2a</td>
<td>0x2b</td>
<td>0x2c</td>
<td>0x2d</td>
<td>0x2e</td>
<td>0x2f</td>
<td>0x30</td>
<td>0x31</td>
</tr>
<tr class="bottom">
<td>42</td>
<td>43</td>
<td>44</td>
<td>45</td>
<td>46</td>
<td>47</td>
<td>48</td>
<td>49</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_YELLOW</th>
<td><span style="background-color:rgb(128, 68, 8)"></span></td>
<td><span style="background-color:rgb(156, 96, 16)"></span></td>
<td><span style="background-color:rgb(184, 120, 24)"></span></td>
<td><span style="background-color:rgb(212, 156, 32)"></span></td>
<td><span style="background-color:rgb(232, 184, 16)"></span></td>
<td><span style="background-color:rgb(252, 212, 0)"></span></td>
<td><span style="background-color:rgb(252, 248, 128)"></span></td>
<td><span style="background-color:rgb(252, 252, 192)"></span></td>
</tr>
<tr>
<td>0x3e</td>
<td>0x3f</td>
<td>0x40</td>
<td>0x41</td>
<td>0x42</td>
<td>0x43</td>
<td>0x44</td>
<td>0x45</td>
</tr>
<tr class="bottom">
<td>62</td>
<td>63</td>
<td>64</td>
<td>65</td>
<td>66</td>
<td>67</td>
<td>68</td>
<td>69</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_RED</th>
<td><span style="background-color:rgb( 92, 0, 0)"></span></td>
<td><span style="background-color:rgb(128, 0, 0)"></span></td>
<td><span style="background-color:rgb(160, 0, 0)"></span></td>
<td><span style="background-color:rgb(196, 0, 0)"></span></td>
<td><span style="background-color:rgb(224, 0, 0)"></span></td>
<td><span style="background-color:rgb(252, 52, 52)"></span></td>
<td><span style="background-color:rgb(252, 100, 88)"></span></td>
<td><span style="background-color:rgb(252, 144, 124)"></span></td>
</tr>
<tr>
<td>0xb3</td>
<td>0xb4</td>
<td>0xb5</td>
<td>0xb6</td>
<td>0xb7</td>
<td>0xa4</td>
<td>0xa5</td>
<td>0xa6</td>
</tr>
<tr class="bottom">
<td>179</td>
<td>180</td>
<td>181</td>
<td>182</td>
<td>183</td>
<td>164</td>
<td>165</td>
<td>166</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_LIGHT_BLUE</th>
<td><span style="background-color:rgb( 16, 64, 96)"></span></td>
<td><span style="background-color:rgb( 24, 80, 108)"></span></td>
<td><span style="background-color:rgb( 40, 96, 120)"></span></td>
<td><span style="background-color:rgb( 52, 112, 132)"></span></td>
<td><span style="background-color:rgb( 80, 140, 160)"></span></td>
<td><span style="background-color:rgb(116, 172, 192)"></span></td>
<td><span style="background-color:rgb(156, 204, 220)"></span></td>
<td><span style="background-color:rgb(204, 240, 252)"></span></td>
</tr>
<tr>
<td>0x9a</td>
<td>0x9b</td>
<td>0x9c</td>
<td>0x9d</td>
<td>0x9e</td>
<td>0x9f</td>
<td>0xa0</td>
<td>0xa1</td>
</tr>
<tr class="bottom">
<td>154</td>
<td>155</td>
<td>156</td>
<td>157</td>
<td>158</td>
<td>159</td>
<td>160</td>
<td>161</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_GREEN</th>
<td><span style="background-color:rgb( 32, 80, 4)"></span></td>
<td><span style="background-color:rgb( 48, 96, 4)"></span></td>
<td><span style="background-color:rgb( 64, 112, 12)"></span></td>
<td><span style="background-color:rgb( 84, 132, 20)"></span></td>
<td><span style="background-color:rgb( 92, 156, 52)"></span></td>
<td><span style="background-color:rgb(108, 176, 64)"></span></td>
<td><span style="background-color:rgb(124, 200, 76)"></span></td>
<td><span style="background-color:rgb(144, 224, 92)"></span></td>
</tr>
<tr>
<td>0x52</td>
<td>0x53</td>
<td>0x54</td>
<td>0x55</td>
<td>0xce</td>
<td>0xcf</td>
<td>0xd0</td>
<td>0xd1</td>
</tr>
<tr class="bottom">
<td>82</td>
<td>83</td>
<td>84</td>
<td>85</td>
<td>206</td>
<td>207</td>
<td>208</td>
<td>209</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_DARK_GREEN</th>
<td><span style="background-color:rgb( 28, 52, 24)"></span></td>
<td><span style="background-color:rgb( 44, 68, 32)"></span></td>
<td><span style="background-color:rgb( 60, 88, 48)"></span></td>
<td><span style="background-color:rgb( 80, 104, 60)"></span></td>
<td><span style="background-color:rgb(104, 124, 76)"></span></td>
<td><span style="background-color:rgb(128, 148, 92)"></span></td>
<td><span style="background-color:rgb(152, 176, 108)"></span></td>
<td><span style="background-color:rgb(180, 204, 124)"></span></td>
</tr>
<tr>
<td>0x58</td>
<td>0x59</td>
<td>0x5a</td>
<td>0x5b</td>
<td>0x5c</td>
<td>0x5d</td>
<td>0x5e</td>
<td>0x5f</td>
</tr>
<tr class="bottom">
<td>88</td>
<td>89</td>
<td>90</td>
<td>91</td>
<td>92</td>
<td>93</td>
<td>94</td>
<td>95</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_BLUE</th>
<td><span style="background-color:rgb( 0, 52, 160)"></span></td>
<td><span style="background-color:rgb( 0, 72, 184)"></span></td>
<td><span style="background-color:rgb( 0, 96, 212)"></span></td>
<td><span style="background-color:rgb( 24, 120, 220)"></span></td>
<td><span style="background-color:rgb( 56, 144, 232)"></span></td>
<td><span style="background-color:rgb( 88, 168, 240)"></span></td>
<td><span style="background-color:rgb(128, 196, 252)"></span></td>
<td><span style="background-color:rgb(188, 224, 252)"></span></td>
</tr>
<tr>
<td>0x92</td>
<td>0x93</td>
<td>0x94</td>
<td>0x95</td>
<td>0x96</td>
<td>0x97</td>
<td>0x98</td>
<td>0x99</td>
</tr>
<tr class="bottom">
<td>146</td>
<td>147</td>
<td>148</td>
<td>149</td>
<td>150</td>
<td>151</td>
<td>152</td>
<td>153</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_CREAM</th>
<td><span style="background-color:rgb(116, 68, 40)"></span></td>
<td><span style="background-color:rgb(136, 84, 56)"></span></td>
<td><span style="background-color:rgb(164, 96, 64)"></span></td>
<td><span style="background-color:rgb(184, 112, 80)"></span></td>
<td><span style="background-color:rgb(204, 128, 96)"></span></td>
<td><span style="background-color:rgb(212, 148, 112)"></span></td>
<td><span style="background-color:rgb(224, 168, 128)"></span></td>
<td><span style="background-color:rgb(236, 188, 148)"></span></td>
</tr>
<tr>
<td>0x72</td>
<td>0x73</td>
<td>0x74</td>
<td>0x75</td>
<td>0x76</td>
<td>0x77</td>
<td>0x78</td>
<td>0x79</td>
</tr>
<tr class="bottom">
<td>114</td>
<td>115</td>
<td>116</td>
<td>117</td>
<td>118</td>
<td>119</td>
<td>120</td>
<td>121</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_MAUVE</th>
<td><span style="background-color:rgb( 36, 40, 68)"></span></td>
<td><span style="background-color:rgb( 48, 52, 84)"></span></td>
<td><span style="background-color:rgb( 64, 64, 100)"></span></td>
<td><span style="background-color:rgb( 80, 80, 116)"></span></td>
<td><span style="background-color:rgb(100, 100, 136)"></span></td>
<td><span style="background-color:rgb(132, 132, 164)"></span></td>
<td><span style="background-color:rgb(172, 172, 192)"></span></td>
<td><span style="background-color:rgb(212, 212, 224)"></span></td>
</tr>
<tr>
<td>0x80</td>
<td>0x81</td>
<td>0x82</td>
<td>0x83</td>
<td>0x84</td>
<td>0x85</td>
<td>0x86</td>
<td>0x87</td>
</tr>
<tr class="bottom">
<td>128</td>
<td>129</td>
<td>130</td>
<td>131</td>
<td>132</td>
<td>133</td>
<td>134</td>
<td>135</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PURPLE</th>
<td><span style="background-color:rgb( 40, 20, 112)"></span></td>
<td><span style="background-color:rgb( 64, 44, 144)"></span></td>
<td><span style="background-color:rgb( 88, 64, 172)"></span></td>
<td><span style="background-color:rgb(104, 76, 196)"></span></td>
<td><span style="background-color:rgb(120, 88, 224)"></span></td>
<td><span style="background-color:rgb(140, 104, 252)"></span></td>
<td><span style="background-color:rgb(160, 136, 252)"></span></td>
<td><span style="background-color:rgb(188, 168, 252)"></span></td>
</tr>
<tr>
<td>0x88</td>
<td>0x89</td>
<td>0x8a</td>
<td>0x8b</td>
<td>0x8c</td>
<td>0x8d</td>
<td>0x8e</td>
<td>0x8f</td>
</tr>
<tr class="bottom">
<td>136</td>
<td>137</td>
<td>138</td>
<td>139</td>
<td>140</td>
<td>141</td>
<td>142</td>
<td>143</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_ORANGE</th>
<td><span style="background-color:rgb(184, 120, 24)"></span></td>
<td><span style="background-color:rgb(204, 136, 8)"></span></td>
<td><span style="background-color:rgb(228, 144, 4)"></span></td>
<td><span style="background-color:rgb(252, 156, 0)"></span></td>
<td><span style="background-color:rgb(252, 176, 48)"></span></td>
<td><span style="background-color:rgb(252, 196, 100)"></span></td>
<td><span style="background-color:rgb(252, 216, 152)"></span></td>
<td><span style="background-color:rgb(244, 220, 176)"></span></td>
</tr>
<tr>
<td>0x40</td>
<td>0xc0</td>
<td>0xc1</td>
<td>0xc2</td>
<td>0xc3</td>
<td>0xc4</td>
<td>0xc5</td>
<td>0x27</td>
</tr>
<tr class="bottom">
<td>64</td>
<td>192</td>
<td>193</td>
<td>194</td>
<td>195</td>
<td>196</td>
<td>197</td>
<td>39</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_BROWN</th>
<td><span style="background-color:rgb( 72, 44, 4)"></span></td>
<td><span style="background-color:rgb( 88, 60, 20)"></span></td>
<td><span style="background-color:rgb(104, 80, 44)"></span></td>
<td><span style="background-color:rgb(124, 104, 72)"></span></td>
<td><span style="background-color:rgb(152, 132, 92)"></span></td>
<td><span style="background-color:rgb(184, 160, 120)"></span></td>
<td><span style="background-color:rgb(212, 188, 148)"></span></td>
<td><span style="background-color:rgb(244, 220, 176)"></span></td>
</tr>
<tr>
<td>0x20</td>
<td>0x21</td>
<td>0x22</td>
<td>0x23</td>
<td>0x24</td>
<td>0x25</td>
<td>0x26</td>
<td>0x27</td>
</tr>
<tr class="bottom">
<td>32</td>
<td>33</td>
<td>34</td>
<td>35</td>
<td>36</td>
<td>37</td>
<td>38</td>
<td>39</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_GREY</th>
<td><span style="background-color:rgb( 64, 64, 64)"></span></td>
<td><span style="background-color:rgb( 80, 80, 80)"></span></td>
<td><span style="background-color:rgb(100, 100, 100)"></span></td>
<td><span style="background-color:rgb(116, 116, 116)"></span></td>
<td><span style="background-color:rgb(132, 132, 132)"></span></td>
<td><span style="background-color:rgb(148, 148, 148)"></span></td>
<td><span style="background-color:rgb(168, 168, 168)"></span></td>
<td><span style="background-color:rgb(184, 184, 184)"></span></td>
</tr>
<tr>
<td>0x4</td>
<td>0x5</td>
<td>0x6</td>
<td>0x7</td>
<td>0x8</td>
<td>0x9</td>
<td>0xa</td>
<td>0xb</td>
</tr>
<tr class="bottom">
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_WHITE</th>
<td><span style="background-color:rgb(132, 132, 132)"></span></td>
<td><span style="background-color:rgb(148, 148, 148)"></span></td>
<td><span style="background-color:rgb(168, 168, 168)"></span></td>
<td><span style="background-color:rgb(184, 184, 184)"></span></td>
<td><span style="background-color:rgb(200, 200, 200)"></span></td>
<td><span style="background-color:rgb(216, 216, 216)"></span></td>
<td><span style="background-color:rgb(232, 232, 232)"></span></td>
<td><span style="background-color:rgb(252, 252, 252)"></span></td>
</tr>
<tr>
<td>0x8</td>
<td>0x9</td>
<td>0xa</td>
<td>0xb</td>
<td>0xc</td>
<td>0xd</td>
<td>0xe</td>
<td>0xf</td>
</tr>
<tr class="bottom">
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</tbody>
</table>
</body>
</html>

83
docs/game_coordinator.md Normal file
View File

@@ -0,0 +1,83 @@
# Game Coordinator
To allow two players to play together, OpenTTD uses a Game Coordinator to
facilitate this.
When a server starts, it can register itself to the Game Coordinator. This
happens when `server_game_type` is set to either `invite-only` or `public`.
Upon registration, the Game Coordinator probes the network of the server, and
assigns the server an unique code, called an `invite code`.
When a client wants to join a server, it asks the Game Coordinator to help
with this by giving it the `invite code` of the server. The Game Coordinator
will, in this order, attempt several ways to connect the client and server
together:
## 1) Via direct IPv4 / IPv6
Upon registration, the Game Coordinator probes the server to see if a
direction connection to the server is possible based on the game port. It
tries this over the public IPv4 and IPv6, as announced by the server.
If either (or both) are successful, a client will always be asked to try
these direct IPs first when it wants to connect to this server.
- If the server is IPv4 only, the client is only asked to connect via IPv4.
- If the server is IPv6 only, the client is only asked to connect via IPv6.
- If the server is both IPv4 and IPv6, the client is asked to connect to to
one of those first. If that fails, it is asked to connect to the other.
Whether it tries IPv4 or IPv6 first, strongly depends on several network
infrastructure related events. The biggest influence is the network
latency over both protocols to the OpenTTD infrastructure.
In the end, if either the server is not reachable directly from the Internet
or the client fails to connect to either one of them, the connection attempt
continues with the next available method.
## 2) Via STUN
When a client wants to join a server via STUN, both the client and server
are asked to create a connection to the STUN server (normally
`stun.openttd.org`) via both IPv4 and IPv6. If the client or server has no
IPv4 or IPv6, it will not make a connection attempt via that protocol.
The STUN server collects the public IPv4 and/or IPv6 of the client and server,
together with the port the connection came in from. For most NAT gateways it
holds true that as long as you use the same local IP + port, your public
IP + port will remain the same, and allow for bi-directional communication.
And this fact is used to later on pair the client and server.
The STUN server reports this information directly to the Game Coordinator
(this in contrast to most STUN implementation, where this information is
first reported back to the peer itself, which has to relay it back to the
coordinator. OpenTTD skips this step, as the STUN server can directly talk to
the Game Coordinator). When the Game Coordinator sees a matching pair (in
terms of IPv4 / IPv6), it will ask both the client and server to connect to
their peer based on this public IP + port.
As the NAT gateway forwards the traffic on the public IP + port to the local
port, this creates a bi-directional socket between client and server. The
connection to the STUN server can now safely be closed, and the client and
server can continue to talk to each other.
Some NAT gateways do not allow this method; for those this attempt will fail,
and this also means that it is not possible to create a connection between
the client and server.
## 3) Via TURN
As a last resort, the Game Coordinator can decide to connect the client and
server together via TURN. TURN is a relay service, relaying the messages
between client and server.
As the client and server can already connect to the Game Coordinator, it is
very likely this is successful.
It is important to note that a relay service has full view of the traffic
send between client and server, and as such it is important that you trust
the relay service used.
For official binaries, this relay service is hosted by openttd.org. The relay
service as hosted by openttd.org only validates it is relaying valid OpenTTD
packets and does no further inspection of the payload itself.
Although in our experience most patch-packs also use the services as offered
by openttd.org, it is possible they use different services. Please be mindful
about this.

View File

@@ -48,6 +48,10 @@ Last updated: 2011-02-16
- click add server
- type in the ip address or hostname
- if you want to add a port use :<port>
- If you want to play and you have the invite code of the game server you
want connect to.
- click add server
- type in the invite code
- Now you can select a company and press: "Join company", to help that company
- Or you can press "Spectate game", to spectate the game
- Or you can press "New company", and start your own company (if there are
@@ -110,9 +114,10 @@ Last updated: 2011-02-16
- You can let your server automatically restart a map when, let's say, year 2030
is reached. See 'set restart_game_date' for detail.
- If you want to be on the server-list, enable Advertising. To do this, select
'Internet (advertise)' in the Start Server menu, or type in console:
'set server_advertise 1'.
- If you want to be on the server-list, make your server public. You can do
this either from the Start Server GUI, via the in-game Online Players GUI,
or by typing in the console:
'set server_game_type public'.
- You can protect your server with a password via the console: 'set server_pw',
or via the Start Server menu.

214
docs/savegame_format.md Normal file
View File

@@ -0,0 +1,214 @@
# OpenTTD's Savegame Format
Last updated: 2021-06-15
## Outer container
Savegames for OpenTTD start with an outer container, to contain the compressed data for the rest of the savegame.
`[0..3]` - The first four bytes indicate what compression is used.
In ASCII, these values are possible:
- `OTTD` - Compressed with LZO (deprecated, only really old savegames would use this).
- `OTTN` - No compression.
- `OTTZ` - Compressed with zlib.
- `OTTX` - Compressed with LZMA.
`[4..5]` - The next two bytes indicate which savegame version used.
`[6..7]` - The next two bytes can be ignored, and were only used in really old savegames.
`[8..N]` - Next follows a binary blob which is compressed with the indicated compression algorithm.
The rest of this document talks about this decompressed blob of data.
## Data types
The savegame is written in Big Endian, so when we talk about a 16-bit unsigned integer (`uint16`), we mean it is stored in Big Endian.
The following types are valid:
- `1` - `int8` / `SLE_FILE_I8` -8-bit signed integer
- `2` - `uint8` / `SLE_FILE_U8` - 8-bit unsigned integer
- `3` - `int16` / `SLE_FILE_I16` - 16-bit signed integer
- `4` - `uint16` / `SLE_FILE_U16` - 16-bit unsigned integer
- `5` - `int32` / `SLE_FILE_I32` - 32-bit signed integer
- `6` - `uint32` / `SLE_FILE_U32` - 32-bit unsigned integer
- `7` - `int64` / `SLE_FILE_I64` - 64-bit signed integer
- `8` - `uint64` / `SLE_FILE_U64` - 64-bit unsigned integer
- `9` - `StringID` / `SLE_FILE_STRINGID` - a StringID inside the OpenTTD's string table
- `10` - `str` / `SLE_FILE_STRING` - a string (prefixed with a length-field)
- `11` - `struct` / `SLE_FILE_STRUCT` - a struct
### Gamma value
There is also a field-type called `gamma`.
This is most often used for length-fields, and uses as few bytes as possible to store an integer.
For values <= 127, it uses a single byte.
For values > 127, it uses two bytes and sets the highest bit to high.
For values > 32767, it uses three bytes and sets the two highest bits to high.
And this continues till the value fits.
In a more visual approach:
```
0xxxxxxx
10xxxxxx xxxxxxxx
110xxxxx xxxxxxxx xxxxxxxx
1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
11110--- xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
```
## Chunks
Savegames for OpenTTD store their data in chunks.
Each chunk contains data for a certain part of the game, for example "Companies", "Vehicles", etc.
`[0..3]` - Each chunk starts with four bytes to indicate the tag.
If the tag is `\x00\x00\x00\x00` it means the end of the savegame is reached.
An example of a valid tag is `PLYR` when looking at it via ASCII, which contains the information of all the companies.
`[4..4]` - Next follows a byte where the lower 4 bits contain the type.
The possible valid types are:
- `0` - `CH_RIFF` - This chunk is a binary blob.
- `1` - `CH_ARRAY` - This chunk is a list of items.
- `2` - `CH_SPARSE_ARRAY` - This chunk is a list of items.
- `3` - `CH_TABLE` - This chunk is self-describing list of items.
- `4` - `CH_SPARSE_TABLE` - This chunk is self-describing list of items.
Now per type the format is (slightly) different.
### CH_RIFF
(since savegame version 295, this chunk type is only used for MAP-chunks, containing bit-information about each tile on the map)
A `CH_RIFF` starts with an `uint24` which together with the upper-bits of the type defines the length of the chunk.
In pseudo-code:
```
type = read uint8
if type == 0
length = read uint24
length |= ((type >> 4) << 24)
```
The next `length` bytes are part of the chunk.
What those bytes mean depends on the tag of the chunk; further details per chunk can be found in the source-code.
### CH_ARRAY / CH_SPARSE_ARRAY
(this chunk type is deprecated since savegame version 295 and is no longer in use)
`[0..G1]` - A `CH_ARRAY` / `CH_SPARSE_ARRAY` starts with a `gamma`, indicating the size of the next item plus one.
If this size value is zero, it indicates the end of the list.
This indicates the full length of the next item minus one.
In psuedo-code:
```
loop
size = read gamma - 1
if size == -1
break loop
read <size> bytes
```
`[]` - For `CH_ARRAY` there is an implicit index.
The loop starts at zero, and every iteration adds one to the index.
For entries in the game that were not allocated, the `size` will be zero.
`[G1+1..G2]` - For `CH_SPARSE_ARRAY` there is an explicit index.
The `gamma` following the size indicates the index.
The content of the item is a binary blob, and similar to `CH_RIFF`, it depends on the tag of the chunk what it means.
Please check the source-code for further details.
### CH_TABLE / CH_SPARSE_TABLE
(this chunk type only exists since savegame version 295)
Both `CH_TABLE` and `CH_SPARSE_TABLE` are very similar to `CH_ARRAY` / `CH_SPARSE_ARRAY` respectively.
The only change is that the chunk starts with a header.
This header describes the chunk in details; with the header you know the meaning of each byte in the binary blob that follows.
`[0..G]` - The header starts with a `gamma` to indicate the size of all the headers in this chunk plus one.
If this size value is zero, it means there is no header, which should never be the case.
Next follows a list of `(type, key)` pairs:
- `[0..0]` - Type of the field.
- `[1..G]` - `gamma` to indicate length of key.
- `[G+1..N]` - Key (in UTF-8) of the field.
If at any point `type` is zero, the list stops (and no `key` follows).
The `type`'s lower 4 bits indicate the data-type (see chapter above).
The `type`'s 5th bit (so `0x10`) indicates if the field is a list, and if this field in every record starts with a `gamma` to indicate how many times the `type` is repeated.
If the `type` indicates either a `struct` or `str`, the `0x10` flag is also always set.
As the savegame format allows (list of) structs in structs, if any `struct` type is found, this header will be followed by a header of that struct.
This nesting of structs is stored depth-first, so given this table:
```
type | key
-----------------
uint8 | counter
struct | substruct1
struct | substruct2
```
With `substruct1` being like:
```
type | key
-----------------
uint8 | counter
struct | substruct3
```
The headers will be, in order: `table`, `substruct1`, `substruct3`, `substruct2`, each ending with a `type` is zero field.
After reading all the fields of all the headers, there is a list of records.
To read this, see `CH_ARRAY` / `CH_SPARSE_ARRAY` for details.
As each `type` has a well defined length, you can read the records even without knowing anything about the chunk-tag yourself.
Do remember, that if the `type` had the `0x10` flag active, the field in the record first has a `gamma` to indicate how many times that `type` is repeated.
#### Guidelines for network-compatible patch-packs
For network-compatible patch-packs (client-side patches that can play together with unpatched clients) we advise to prefix the field-name with `__<shortname>` when introducing new fields to an existing chunk.
Example: you have an extra setting called `auto_destroy_rivers` you want to store in the savegame for your patched client called `mypp`.
We advise you to call this setting `__mypp_auto_destroy_rivers` in the settings chunk.
Doing it this way ensures that a savegame created by these patch-packs can still safely be loaded by unpatched clients.
They will simply ignore the field and continue loading the savegame as usual.
The prefix is strongly advised to avoid conflicts with future-settings in an unpatched client or conflicts with other patch-packs.
## Scripts custom data format
Script chunks (`AIPL` and `GSDT`) use `CH_TABLE` chunk type.
At the end of each record there's an `uint8` to indicate if there's custom data (1) or not (0).
There are 6 data types for scripts, called `script-data-type`.
When saving, each `script-data-type` starts with the type marker saved as `uint8` followed by the actual data.
- `0` - `SQSL_INT`:
- an `int64` with the actual value (`int32` before savegame version 296).
- `1` - `SQSL_STRING`:
- an `uint8` with the string length.
- a list of `int8` for the string itself.
- `2` - `SQSL_ARRAY`:
- each element saved as `script-data-type`.
- an `SQSL_ARRAY_TABLE_END` (0xFF) marker (`uint8`).
- `3` - `SQSL_TABLE`:
- for each element:
- key saved as `script-data-type`.
- value saved as `script-data-type`.
- an `SQSL_ARRAY_TABLE_END` (0xFF) marker (`uint8`).
- `4` - `SQSL_BOOL`:
- an `uint8` with 0 (false) or 1 (true).
- `5` - `SQSL_NULL`:
- (no data follows)
The first data type is always a `SQSL_TABLE`.