Commit e0aadec964

e0aadec964e4faa14fcad919ab343c08e05512d4

parent: cd22a15611

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-20 16:56 UTC

fix: update glyphs, encoding, commands, and add content to How It Works section

Layout: unified · split

content/blog/2026-08-20-nfogen.org +150 −51
@@ -18,51 +18,56 @@ credits, installation instructions, BBS/contact info, greetz/notes, etc.
1818For example, here's the =bonzai.nfo= file for a patched save game for Lemmings
1919III, with credits to those involved and instructions to get in touch.
2020
21/Note: The [[http://www.textfiles.com/piracy/NFO/bonzai.nfo][original file]] on textfiles.com contains CP437 bytes decoded as
22Cyrillic, so I converted this example correctly to CP437. If you view the online
23version, you'll notice that the box drawing and group logo are incomprehensible,
24which proves my point later on in the CP437 section./
25
2126#+begin_src nfo
22 К К
23
24 лллллК К
25 лллллК К
26 лллллпллл лллллпллл лллллпллл лллллпллл лллллпллл ппппп
27 лллллКлллл ллллл лллл ллллл лллл ллллл лллл ллллл лллл ллллл
28 лллллКлллл ллллл лллл ллллл лллл ммммммлллл лллллмлллл лллллл
29 лллллКлллл ллллл лллл ллллл лллл ллллл мммм ллллл лллл лллллл
30 ллллл лллл ллллл лллл ллллл лллл ллллл лллл ллллл лллл лллллл
31 лллллКлллл ллллл лллл ллллл лллл ллллл лллл ллллл лллл лллллл
32 лллллКлллл ллллл лллл ллллл лллл ллллл лллл ллллл лллл лллллл
33 ллллл лллл ллллл лллл ллллл лллл ллллл лллл ллллл лллл лллллл
34 пппппппппп пппппппппп ппппп пппп пппппппппп ппппп пппп пппппп
35 К жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ К
36 К ў BONZAi Ф EVERYTHiNG BUT FRiENDSHiP ў К
37 К гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН К
27 ║ ║
28
29 █████║ ║
30 █████║ ║
31 █████▀███ █████▀███ █████▀███ █████▀███ █████▀███ ▀▀▀▀▀
32 █████║████ █████ ████ █████ ████ █████ ████ █████ ████ █████
33 █████║████ █████ ████ █████ ████ ▄▄▄▄▄▄████ █████▄████ ██████
34 █████║████ █████ ████ █████ ████ █████ ▄▄▄▄ █████ ████ ██████
35 █████ ████ █████ ████ █████ ████ █████ ████ █████ ████ ██████
36 █████║████ █████ ████ █████ ████ █████ ████ █████ ████ ██████
37 █████║████ █████ ████ █████ ████ █████ ████ █████ ████ ██████
38 █████ ████ █████ ████ █████ ████ █████ ████ █████ ████ ██████
39 ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀
40 ║ ╓────────────────────────────────────────╖ ║
41 ║ ■ BONZAi ─ EVERYTHiNG BUT FRiENDSHiP ■ ║
42 ║ ╙────────────────────────────────────────╜ ║
3843 R E L E A S E D :
39 К К
40 К 9O BYTES SAVE GAME PATCHER FOR LEMMiNGS III К
41
42 К FiLENAME .............. : ........... BNZ-L3T.ZiP К
43 К TYPE .................. : ............... TRAiNER К
44 К COMPANY ............... : ............. PSYGNOSiS К
45 К CODER .. .............. : ................ SCAMPY К
46 К PACKAGER .............. : ................ SCAMPY К
47 К DATE AT RELEASiNG ..... : ............ 11-27-1994 К
48 К TiME AT RELEASiNG ..... : .............. 15:OO PM К
49 К RELEASE NOTES : ..................... READ BELOW! К
50 К К
51 К !!!*!!! IMPORTANT !!!*!!! К
52 К К
53 К NiCE PATCHER FOR A NiCE GAME - THiS PATCHER WAS К
54 К KINDA DiFFiCULT BECAUSE OF THE MANY BYTES TO BE К
55 К PATCHED. BUT PROBLEMS WiTH PATCHiNG TiME AND К
56 К FiLE LENGTH HAVE BEEN SOLVED. К
57 К К
58 К ENJOY PLAYiN' -SCAMPY! К
59 К К
60 К К
61 К MEMBERS OF BONZAi ARE . : К
62 К MOTiV8- SCAMPY- SUM-1- DEViL К
63 К К
64 К BULLETiN BOARD SYSTEMS : К
65Э Э ЭЭ ЭЭЭМ NONE! SEARCHiNG! ШЭЭЭ ЭЭ Э Э
44 ║ ║
45 ║ 9O BYTES SAVE GAME PATCHER FOR LEMMiNGS III ║
46
47 ║ FiLENAME .............. : ........... BNZ-L3T.ZiP ║
48 ║ TYPE .................. : ............... TRAiNER ║
49 ║ COMPANY ............... : ............. PSYGNOSiS ║
50 ║ CODER .. .............. : ................ SCAMPY ║
51 ║ PACKAGER .............. : ................ SCAMPY ║
52 ║ DATE AT RELEASiNG ..... : ............ 11-27-1994 ║
53 ║ TiME AT RELEASiNG ..... : .............. 15:OO PM ║
54 ║ RELEASE NOTES : ..................... READ BELOW! ║
55 ║ ║
56 ║ !!!*!!! IMPORTANT !!!*!!! ║
57 ║ ║
58 ║ NiCE PATCHER FOR A NiCE GAME - THiS PATCHER WAS ║
59 ║ KINDA DiFFiCULT BECAUSE OF THE MANY BYTES TO BE ║
60 ║ PATCHED. BUT PROBLEMS WiTH PATCHiNG TiME AND ║
61 ║ FiLE LENGTH HAVE BEEN SOLVED. ║
62 ║ ║
63 ║ ENJOY PLAYiN' -SCAMPY! ║
64 ║ ║
65 ║ ║
66 ║ MEMBERS OF BONZAi ARE . : ║
67 ║ MOTiV8- SCAMPY- SUM-1- DEViL ║
68 ║ ║
69 ║ BULLETiN BOARD SYSTEMS : ║
70═ ═ ══ ═══╝ NONE! SEARCHiNG! ╚═══ ══ ═ ═
6671
6772 GREETiNGS TO:
6873 TOXiC HYBRiD PENTAGRAM PARADOX THHG G.T.A TRSI
@@ -72,7 +77,7 @@ III, with credits to those involved and instructions to get in touch.
7277 CURRENTLY WE ARE SEARCHiNG FOR ALL KiND OF MEMBERS.. LiKE TRADERS
7378 SUPPLiERS.. SYSOPS.. ETC.
7479
75Э Э ЭЭ ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ ЭЭ Э Э
80═ ═ ══ ══════════════════════════════════════════════════════════ ══ ═ ═
7681#+end_src
7782
7883If you want to browse an archive of NFO files, head over to [[http://www.textfiles.com/piracy/NFO/][textfiles]].
@@ -95,7 +100,7 @@ use code blocks, like I did above).
95100One such character set was often used for NFO files: IBM's [[https://en.wikipedia.org/wiki/Code_page_437][CP437]] character set,
96101released with the 1981 IBM PC. This set was popular because each character could
97102be typed with an alt code in DOS, even if they were not on the user's keyboard.
98E.g., =Alt + 2665= would result in the following character: =♥=.
103E.g., =Alt + 3= would result in the following character: =♥=.
99104
100105* Introducing: nfogen
101106
@@ -104,7 +109,8 @@ recently. As part of this, I started using NFO files for my git repos under my
104109new org and wanted a tool that could help me accelerate the generation of these
105110files.
106111
107Introducing [[https://github.com/krazywarez/nfogen][nfogen]], a small Python utility that can generate NFO files for you based on:
112Introducing [[https://github.com/krazywarez/nfogen][nfogen]], a small Python utility that can generate NFO files for you
113based on:
108114- templates
109115- profiles
110116- configuration files
@@ -172,7 +178,7 @@ generated by nfogen: nfogen.py -c nfogen.toml
172178Next, I ran the following command, which produced the NFO file below:
173179
174180#+begin_src shell
175nfogen.py -c nfogen.toml -p -o README.nfo
181nfogen.py -c nfogen.toml -o README.nfo
176182#+end_src
177183
178184#+begin_src nfo
@@ -230,12 +236,11 @@ For more examples, browse the [[https://github.com/krazywarez/nfogen/tree/main/e
230236* Usage
231237** Installation
232238
233The fastest installation method is to clone the repo and use the Python file
234directly:
239The fastest installation method is to fetch and use the Python file directly:
235240
236241#+begin_src shell
237git clone https://github.com/krazywarez/nfogen.git && cd nfogen
238nfogen.py -i
242curl -O https://raw.githubusercontent.com/krazywarez/nfogen/main/nfogen.py
243python3 nfogen.py -i
239244#+end_src
240245
241246** Generating a File
@@ -262,9 +267,103 @@ nfogen itself works by parsing the various inputs, determining a style to use
262267for the box-drawing/character sets, and rendering the characters individually by
263268rows and columns.
264269
270** Width
271
272The first issue I ran into was determining the width to use. I settled on 79
273characters, with border characters at both ends, because X. This means that my
274script needs to perform some math to calculate wrapping, padding, and centering
275of text throughout.
276
277The math is relatively simple because CP437 is single-byte and single-width,
278which means I can write functions that depend on that one-to-one correlation
279to calculate the spacing properly.
280
281=_Canvas.__init__= defines a few key variables for width:
282
283#+begin_src python
284self.width = width
285self.inner = width - 4 # single-column content width
286self.cl = (width - 7) // 2 # left panel content width
287self.cr = (width - 7) - self.cl
288self.rfill = width - self.cl - 5
289#+end_src
290
291At a width of 79 characters, this breaks down into:
292
293| Variable | Value | What it covers |
294|------------+-------+---------------------------------------------------------|
295| =self.width= | 79 | Total line width, borders included |
296| =self.inner= | 75 | Single-column content, minus two verticals and two pads |
297| =self.cl= | 36 | Left panel content width |
298| =self.cr= | 36 | Right panel content, taking the remainder |
299| =self.rfill= | 38 | Rule segment right of the joint, =cr= plus its two pads |
300
301These combine into a cohesive 79-chararacter-width NFO file with consistent
302padding and alignment.
303
304** Character Sets
305
306=nfogen= currently supports two different character sets as outputs: UTF-8 and
307CP437. For example, there are =╔ ═ ║= characters in the UTF-8 example, but a real
308CP437 file contains the single bytes =0xC9 0xCD 0xBA=.
309
310However, this doesn't really change the core generation of the NFO file. The
311file itself is charset-independent and the charset only comes into play during
312the final encode of the file.
313
314Referring back to the =bonzai.nfo= file above, it was decoded improperly as
315ISO-8859-5, which resulted in Cyrillic characters instead of the proper charset.
316Defining an appropriate charset and encoding/decoding is critical to ensuring
317the longevity of NFO files.
318
319** Banner Generator
320
321The banner generator is a function by itself, which renders independent from the
322rest of the file and separate from the box geometry. This function defines a set
323of glyphs, takes an input text string from the user, and maps each character to
324its glyph. The result is rendered above the box and is centered, so it does not
325depend on the math applied to the canvas.
326
327If an explicity art file is defined, this generator does not run.
328
329#+begin_src python
330# A compact 5-row block font for the banner generator. Each glyph is five
331# rows joined by "/". Uppercase letters, digits, space, and [<35;44;2Ma few symbols.
332_FONT = {
333 "A": " ### /# #/#####/# #/# #", "B": "#### /# #/#### /# #/#### ",
334 "C": " ####/# /# /# / ####", "D": "#### /# #/# #/# #/#### ",
335 # ... through Z, 0-9, space, -, ., !, :
336}
337
338
339def banner(text: str) -> str:
340 """Render text as a 5-row ASCII banner using the built-in block font."""
341 rows = ["", "", "", "", ""]
342 for ch in text.upper():
343 glyph = _FONT.get(ch, _FONT[" "]).split("/")
344 for i in range(5):
345 rows[i] += glyph[i] + " "
346 return "\n".join(r.rstrip() for r in rows)
347#+end_src
348
349** Resolution Order
350
351Finally, I defined a specific order to the inputs. As stated in the README, the
352order of importance is: =template < profile < config file < CLI flags < prompts=.
353
354This was an opinionated design choice, as I felt that if you were to create a
355template, that should take precedence above all. Then, profiles and
356configurations are additional preconfigured files that require effort to create
357and maintain, which earns them the next spots. Finally, the script falls back to
358CLI arguments, if supplied, and then interactive prompt mode if nothing else
359exists.
360
265361* The Future
266362
267363I'd love to build this tool out to support more character sets, pre-built
268364libraries for ASCII art and table layouts, and more customization options.
269365
366For now, the tool works and is sufficient for my personal use cases, so I'm
367content with it.
368
270369If you're interested in helping, feel free to reach out!