Commit e0aadec964
Verified · cmc
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. | ||
| 18 | 18 | For example, here's the =bonzai.nfo= file for a patched save game for Lemmings |
| 19 | 19 | III, with credits to those involved and instructions to get in touch. |
| 20 | 20 | |
| 21 | /Note: The [[http://www.textfiles.com/piracy/NFO/bonzai.nfo][original file]] on textfiles.com contains CP437 bytes decoded as | |
| 22 | Cyrillic, so I converted this example correctly to CP437. If you view the online | |
| 23 | version, you'll notice that the box drawing and group logo are incomprehensible, | |
| 24 | which proves my point later on in the CP437 section./ | |
| 25 | ||
| 21 | 26 | #+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 | ║ ╙────────────────────────────────────────╜ ║ | |
| 38 | 43 | 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! ╚═══ ══ ═ ═ | |
| 66 | 71 | |
| 67 | 72 | GREETiNGS TO: |
| 68 | 73 | 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. | ||
| 72 | 77 | CURRENTLY WE ARE SEARCHiNG FOR ALL KiND OF MEMBERS.. LiKE TRADERS |
| 73 | 78 | SUPPLiERS.. SYSOPS.. ETC. |
| 74 | 79 | |
| 75 | Э Э ЭЭ ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ ЭЭ Э Э | |
| 80 | ═ ═ ══ ══════════════════════════════════════════════════════════ ══ ═ ═ | |
| 76 | 81 | #+end_src |
| 77 | 82 | |
| 78 | 83 | If 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). | ||
| 95 | 100 | One such character set was often used for NFO files: IBM's [[https://en.wikipedia.org/wiki/Code_page_437][CP437]] character set, |
| 96 | 101 | released with the 1981 IBM PC. This set was popular because each character could |
| 97 | 102 | be typed with an alt code in DOS, even if they were not on the user's keyboard. |
| 98 | E.g., =Alt + 2665= would result in the following character: =♥=. | |
| 103 | E.g., =Alt + 3= would result in the following character: =♥=. | |
| 99 | 104 | |
| 100 | 105 | * Introducing: nfogen |
| 101 | 106 | |
| @@ -104,7 +109,8 @@ recently. As part of this, I started using NFO files for my git repos under my | ||
| 104 | 109 | new org and wanted a tool that could help me accelerate the generation of these |
| 105 | 110 | files. |
| 106 | 111 | |
| 107 | Introducing [[https://github.com/krazywarez/nfogen][nfogen]], a small Python utility that can generate NFO files for you based on: | |
| 112 | Introducing [[https://github.com/krazywarez/nfogen][nfogen]], a small Python utility that can generate NFO files for you | |
| 113 | based on: | |
| 108 | 114 | - templates |
| 109 | 115 | - profiles |
| 110 | 116 | - configuration files |
| @@ -172,7 +178,7 @@ generated by nfogen: nfogen.py -c nfogen.toml | ||
| 172 | 178 | Next, I ran the following command, which produced the NFO file below: |
| 173 | 179 | |
| 174 | 180 | #+begin_src shell |
| 175 | nfogen.py -c nfogen.toml -p -o README.nfo | |
| 181 | nfogen.py -c nfogen.toml -o README.nfo | |
| 176 | 182 | #+end_src |
| 177 | 183 | |
| 178 | 184 | #+begin_src nfo |
| @@ -230,12 +236,11 @@ For more examples, browse the [[https://github.com/krazywarez/nfogen/tree/main/e | ||
| 230 | 236 | * Usage |
| 231 | 237 | ** Installation |
| 232 | 238 | |
| 233 | The fastest installation method is to clone the repo and use the Python file | |
| 234 | directly: | |
| 239 | The fastest installation method is to fetch and use the Python file directly: | |
| 235 | 240 | |
| 236 | 241 | #+begin_src shell |
| 237 | git clone https://github.com/krazywarez/nfogen.git && cd nfogen | |
| 238 | nfogen.py -i | |
| 242 | curl -O https://raw.githubusercontent.com/krazywarez/nfogen/main/nfogen.py | |
| 243 | python3 nfogen.py -i | |
| 239 | 244 | #+end_src |
| 240 | 245 | |
| 241 | 246 | ** Generating a File |
| @@ -262,9 +267,103 @@ nfogen itself works by parsing the various inputs, determining a style to use | ||
| 262 | 267 | for the box-drawing/character sets, and rendering the characters individually by |
| 263 | 268 | rows and columns. |
| 264 | 269 | |
| 270 | ** Width | |
| 271 | ||
| 272 | The first issue I ran into was determining the width to use. I settled on 79 | |
| 273 | characters, with border characters at both ends, because X. This means that my | |
| 274 | script needs to perform some math to calculate wrapping, padding, and centering | |
| 275 | of text throughout. | |
| 276 | ||
| 277 | The math is relatively simple because CP437 is single-byte and single-width, | |
| 278 | which means I can write functions that depend on that one-to-one correlation | |
| 279 | to calculate the spacing properly. | |
| 280 | ||
| 281 | =_Canvas.__init__= defines a few key variables for width: | |
| 282 | ||
| 283 | #+begin_src python | |
| 284 | self.width = width | |
| 285 | self.inner = width - 4 # single-column content width | |
| 286 | self.cl = (width - 7) // 2 # left panel content width | |
| 287 | self.cr = (width - 7) - self.cl | |
| 288 | self.rfill = width - self.cl - 5 | |
| 289 | #+end_src | |
| 290 | ||
| 291 | At 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 | ||
| 301 | These combine into a cohesive 79-chararacter-width NFO file with consistent | |
| 302 | padding and alignment. | |
| 303 | ||
| 304 | ** Character Sets | |
| 305 | ||
| 306 | =nfogen= currently supports two different character sets as outputs: UTF-8 and | |
| 307 | CP437. For example, there are =╔ ═ ║= characters in the UTF-8 example, but a real | |
| 308 | CP437 file contains the single bytes =0xC9 0xCD 0xBA=. | |
| 309 | ||
| 310 | However, this doesn't really change the core generation of the NFO file. The | |
| 311 | file itself is charset-independent and the charset only comes into play during | |
| 312 | the final encode of the file. | |
| 313 | ||
| 314 | Referring back to the =bonzai.nfo= file above, it was decoded improperly as | |
| 315 | ISO-8859-5, which resulted in Cyrillic characters instead of the proper charset. | |
| 316 | Defining an appropriate charset and encoding/decoding is critical to ensuring | |
| 317 | the longevity of NFO files. | |
| 318 | ||
| 319 | ** Banner Generator | |
| 320 | ||
| 321 | The banner generator is a function by itself, which renders independent from the | |
| 322 | rest of the file and separate from the box geometry. This function defines a set | |
| 323 | of glyphs, takes an input text string from the user, and maps each character to | |
| 324 | its glyph. The result is rendered above the box and is centered, so it does not | |
| 325 | depend on the math applied to the canvas. | |
| 326 | ||
| 327 | If 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 | ||
| 339 | def 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 | ||
| 351 | Finally, I defined a specific order to the inputs. As stated in the README, the | |
| 352 | order of importance is: =template < profile < config file < CLI flags < prompts=. | |
| 353 | ||
| 354 | This was an opinionated design choice, as I felt that if you were to create a | |
| 355 | template, that should take precedence above all. Then, profiles and | |
| 356 | configurations are additional preconfigured files that require effort to create | |
| 357 | and maintain, which earns them the next spots. Finally, the script falls back to | |
| 358 | CLI arguments, if supplied, and then interactive prompt mode if nothing else | |
| 359 | exists. | |
| 360 | ||
| 265 | 361 | * The Future |
| 266 | 362 | |
| 267 | 363 | I'd love to build this tool out to support more character sets, pre-built |
| 268 | 364 | libraries for ASCII art and table layouts, and more customization options. |
| 269 | 365 | |
| 366 | For now, the tool works and is sufficient for my personal use cases, so I'm | |
| 367 | content with it. | |
| 368 | ||
| 270 | 369 | If you're interested in helping, feel free to reach out! |