libaaruformat
1.0
Aaru Data Preservation Suite - Format Library
Toggle main menu visibility
Loading...
Searching...
No Matches
lfg.c
Go to the documentation of this file.
1
/*
2
* This file is part of the Aaru Data Preservation Suite.
3
* Copyright (c) 2019-2026 Natalia Portillo.
4
*
5
* This library is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU Lesser General Public License as
7
* published by the Free Software Foundation; version 2.1 of the License.
8
*
9
* This library is distributed in the hope that it will be useful, but
10
* WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
13
*
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, see
16
* <https://www.gnu.org/licenses/>.
17
*
18
* Lagged Fibonacci Generator for Nintendo GameCube/Wii junk fill.
19
* Based on Dolphin emulator's LaggedFibonacciGenerator (CC0 licensed).
20
*/
21
22
#include "
lfg.h
"
23
24
#include <string.h>
25
26
static
inline
uint32_t
swap32
(uint32_t x)
27
{
return
((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) | ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000); }
28
29
static
void
lfg_forward
(
struct
ngc_lfg_ctx
*ctx)
30
{
31
for
(
size_t
i = 0; i <
NGC_LFG_J
; i++) ctx->
buffer
[i] ^= ctx->
buffer
[i +
NGC_LFG_K
-
NGC_LFG_J
];
32
33
for
(
size_t
i =
NGC_LFG_J
; i <
NGC_LFG_K
; i++) ctx->
buffer
[i] ^= ctx->
buffer
[i -
NGC_LFG_J
];
34
}
35
36
static
void
lfg_backward
(
struct
ngc_lfg_ctx
*ctx,
size_t
start_word,
size_t
end_word)
37
{
38
size_t
loop_end =
NGC_LFG_J
> start_word ?
NGC_LFG_J
: start_word;
39
size_t
upper = end_word <
NGC_LFG_K
? end_word :
NGC_LFG_K
;
40
41
for
(
size_t
i = upper; i > loop_end; --i) ctx->
buffer
[i - 1] ^= ctx->
buffer
[i - 1 -
NGC_LFG_J
];
42
43
size_t
upper2 = end_word <
NGC_LFG_J
? end_word :
NGC_LFG_J
;
44
45
for
(
size_t
i = upper2; i > start_word; --i) ctx->
buffer
[i - 1] ^= ctx->
buffer
[i - 1 +
NGC_LFG_K
-
NGC_LFG_J
];
46
}
47
48
static
bool
lfg_initialize
(
struct
ngc_lfg_ctx
*ctx,
bool
check_existing)
49
{
50
for
(
size_t
i =
NGC_LFG_SEED_SIZE
; i <
NGC_LFG_K
; i++)
51
{
52
uint32_t calculated = (ctx->
buffer
[i - 17] << 23) ^ (ctx->
buffer
[i - 16] >> 9) ^ ctx->
buffer
[i - 1];
53
54
if
(check_existing)
55
{
56
uint32_t actual = (ctx->
buffer
[i] & 0xFF00FFFF) | (ctx->
buffer
[i] << 2 & 0x00FC0000);
57
58
if
((calculated & 0xFFFCFFFF) != actual)
return
false
;
59
}
60
61
ctx->
buffer
[i] = calculated;
62
}
63
64
/* Apply the shift-by-18-instead-of-16 quirk + byteswap */
65
for
(
size_t
i = 0; i <
NGC_LFG_K
; i++)
66
ctx->
buffer
[i] =
swap32
((ctx->
buffer
[i] & 0xFF00FFFF) | ((ctx->
buffer
[i] >> 2) & 0x00FF0000));
67
68
for
(
int
i = 0; i < 4; i++)
lfg_forward
(ctx);
69
70
return
true
;
71
}
72
73
void
ngc_lfg_set_seed
(
struct
ngc_lfg_ctx
*ctx,
const
uint32_t seed[
NGC_LFG_SEED_SIZE
])
74
{
75
ctx->
position_bytes
= 0;
76
77
for
(
size_t
i = 0; i <
NGC_LFG_SEED_SIZE
; i++) ctx->
buffer
[i] =
swap32
(seed[i]);
78
79
lfg_initialize
(ctx,
false
);
80
}
81
82
void
ngc_lfg_get_bytes
(
struct
ngc_lfg_ctx
*ctx, uint8_t *out,
size_t
count)
83
{
84
while
(count > 0)
85
{
86
size_t
avail =
NGC_LFG_K
*
sizeof
(uint32_t) - ctx->
position_bytes
;
87
size_t
chunk = count < avail ? count : avail;
88
89
memcpy(out, (uint8_t *)ctx->
buffer
+ ctx->
position_bytes
, chunk);
90
91
ctx->
position_bytes
+= chunk;
92
count -= chunk;
93
out += chunk;
94
95
if
(ctx->
position_bytes
==
NGC_LFG_K
*
sizeof
(uint32_t))
96
{
97
lfg_forward
(ctx);
98
ctx->
position_bytes
= 0;
99
}
100
}
101
}
102
103
static
bool
lfg_reinitialize
(
struct
ngc_lfg_ctx
*ctx, uint32_t seed_out[
NGC_LFG_SEED_SIZE
])
104
{
105
for
(
int
i = 0; i < 4; i++)
lfg_backward
(ctx, 0,
NGC_LFG_K
);
106
107
for
(
size_t
i = 0; i <
NGC_LFG_K
; i++) ctx->
buffer
[i] =
swap32
(ctx->
buffer
[i]);
108
109
/* Reconstruct bits lost by the shift-by-18-instead-of-16 quirk */
110
for
(
size_t
i = 0; i <
NGC_LFG_SEED_SIZE
; i++)
111
{
112
ctx->
buffer
[i] = (ctx->
buffer
[i] & 0xFF00FFFF) | (ctx->
buffer
[i] << 2 & 0x00FC0000) |
113
((ctx->
buffer
[i + 16] ^ ctx->
buffer
[i + 15]) << 9 & 0x00030000);
114
}
115
116
for
(
size_t
i = 0; i <
NGC_LFG_SEED_SIZE
; i++) seed_out[i] =
swap32
(ctx->
buffer
[i]);
117
118
return
lfg_initialize
(ctx,
true
);
119
}
120
121
size_t
ngc_lfg_get_seed
(
const
uint8_t *data,
size_t
size,
size_t
data_offset, uint32_t seed_out[
NGC_LFG_SEED_SIZE
])
122
{
123
/* Alignment: data - data_offset must be 4-byte aligned */
124
if
(((uintptr_t)data - data_offset) %
sizeof
(uint32_t) != 0)
return
0;
125
126
/* Work on whole u32 words */
127
size_t
bytes_to_skip = ((data_offset + 3) & ~(size_t)3) - data_offset;
128
129
if
(bytes_to_skip > size)
return
0;
130
131
const
uint32_t *u32_data = (
const
uint32_t *)(data + bytes_to_skip);
132
size_t
u32_size = (size - bytes_to_skip) /
sizeof
(uint32_t);
133
size_t
u32_data_offset = (data_offset + bytes_to_skip) /
sizeof
(uint32_t);
134
135
if
(u32_size <
NGC_LFG_K
)
return
0;
136
137
/* Quick sanity check: the top bits have a specific pattern from the shift quirk */
138
for
(
size_t
i = 0; i <
NGC_LFG_K
; i++)
139
{
140
uint32_t x =
swap32
(u32_data[i]);
141
142
if
((x & 0x00C00000) != (x >> 2 & 0x00C00000))
return
0;
143
}
144
145
struct
ngc_lfg_ctx
lfg;
146
size_t
data_offset_mod_k = u32_data_offset %
NGC_LFG_K
;
147
size_t
data_offset_div_k = u32_data_offset /
NGC_LFG_K
;
148
149
/* Place the data into the buffer at the correct position.
150
* Copy raw native-endian u32 values — NO byte-swapping here.
151
* The swap happens later inside lfg_reinitialize/lfg_initialize. */
152
size_t
first_part =
NGC_LFG_K
- data_offset_mod_k;
153
154
if
(first_part >
NGC_LFG_K
) first_part =
NGC_LFG_K
;
155
156
for
(
size_t
i = 0; i < first_part && i <
NGC_LFG_K
; i++) lfg.
buffer
[data_offset_mod_k + i] = u32_data[i];
157
158
for
(
size_t
i = 0; i < data_offset_mod_k; i++) lfg.
buffer
[i] = u32_data[first_part + i];
159
160
lfg_backward
(&lfg, 0, data_offset_mod_k);
161
162
for
(
size_t
i = 0; i < data_offset_div_k; i++)
lfg_backward
(&lfg, 0,
NGC_LFG_K
);
163
164
if
(!
lfg_reinitialize
(&lfg, seed_out))
return
0;
165
166
lfg.
position_bytes
= data_offset % (
NGC_LFG_K
*
sizeof
(uint32_t));
167
168
/* Advance the LFG forward to match the data_offset position. */
169
for
(
size_t
i = 0; i < data_offset_div_k; i++)
lfg_forward
(&lfg);
170
171
/* Count how many bytes from data match the LFG output */
172
size_t
result = 0;
173
const
uint8_t *p = data;
174
const
uint8_t *end = data + size;
175
176
while
(p < end)
177
{
178
uint8_t expected = ((uint8_t *)lfg.
buffer
)[lfg.
position_bytes
];
179
180
if
(*p != expected)
break
;
181
182
result++;
183
p++;
184
lfg.
position_bytes
++;
185
186
if
(lfg.
position_bytes
==
NGC_LFG_K
*
sizeof
(uint32_t))
187
{
188
lfg_forward
(&lfg);
189
lfg.
position_bytes
= 0;
190
}
191
}
192
193
return
result;
194
}
ngc_lfg_get_bytes
void ngc_lfg_get_bytes(struct ngc_lfg_ctx *ctx, uint8_t *out, size_t count)
Generate count bytes of junk data into out.
Definition
lfg.c:82
swap32
static uint32_t swap32(uint32_t x)
Definition
lfg.c:26
lfg_backward
static void lfg_backward(struct ngc_lfg_ctx *ctx, size_t start_word, size_t end_word)
Definition
lfg.c:36
lfg_initialize
static bool lfg_initialize(struct ngc_lfg_ctx *ctx, bool check_existing)
Definition
lfg.c:48
lfg_forward
static void lfg_forward(struct ngc_lfg_ctx *ctx)
Definition
lfg.c:29
lfg_reinitialize
static bool lfg_reinitialize(struct ngc_lfg_ctx *ctx, uint32_t seed_out[17])
Definition
lfg.c:103
ngc_lfg_set_seed
void ngc_lfg_set_seed(struct ngc_lfg_ctx *ctx, const uint32_t seed[17])
Initialize the LFG from a 17-word big-endian seed.
Definition
lfg.c:73
ngc_lfg_get_seed
size_t ngc_lfg_get_seed(const uint8_t *data, size_t size, size_t data_offset, uint32_t seed_out[17])
Try to extract the LFG seed from a chunk of data.
Definition
lfg.c:121
lfg.h
NGC_LFG_K
#define NGC_LFG_K
LFG buffer size (number of uint32 words in state).
Definition
lfg.h:34
NGC_LFG_SEED_SIZE
#define NGC_LFG_SEED_SIZE
Number of uint32 words needed to seed the LFG.
Definition
lfg.h:36
NGC_LFG_J
#define NGC_LFG_J
LFG second tap.
Definition
lfg.h:35
ngc_lfg_ctx
LFG context holding the 521-word circular buffer and byte position.
Definition
lfg.h:42
ngc_lfg_ctx::position_bytes
size_t position_bytes
Definition
lfg.h:44
ngc_lfg_ctx::buffer
uint32_t buffer[521]
Definition
lfg.h:43
src
ngcw
lfg.c
Generated by
1.18.0