Dec 042015
 

Taranis RAID-6 stats logoAnd another continuation after the “final” [part 4]: Unbelievable but true, I actually took the time to re-write and polish the previously broken web reporting for my new RAID-6 array. Originally, it was relying on the 3ware command line tool tw_cli.exe and SmartMonTools’ smartctl.exe (The tools also exist on Linux/UNIX by the way) in conjunction with Windows Batch and some Perl. Rest assured, it was ugly. But in all my glorious stupidity I decided to write the new version in Windows Batch again, this time taking the help of a different set of UNIX-style tools, namely grep.exe, sed.exe, tr.exe and cut.exe. I got all of this stuff on my Windows box anyway, partly from [GnuWin32] and partly out of [CygWin x64], so yeah. Ah yes, and Arecas own cli.exe of course.

Why wouldn’t I use a proper scripting language then? Pffh, I guess I just have some masochistic tendencies…

In any case, results first, you can see the reporting website [by clicking here]. It’ll show you the RAID controller status, some information about the RAID volume and some S.M.A.R.T. info about the individual disks. It’s not real-time as this would consume too many resources, so it’s updated on a daily basis only. Quite nice that SmartMonTools can use Arecas API via things like e.g. smartctl -x -d areca,n/2 /dev/arcmsr0 to get info from disk n (replace with a number)! Here’s a screenshot as well:

Taranis RAID-6 web report

Taranis RAID-6 web report

And the script itself? Well, get ready for some extremely ugly fuckshit, here it comes:

expand/collapse source code (hereby released under the GNU GPLv3)
  1. @ECHO OFF
  2.  
  3. SETLOCAL ENABLEDELAYEDEXPANSION
  4. SET totalreadvolume=0
  5. SET totalwritevolume=0
  6. SET totalreadTiB=0
  7. SET totalwriteTiB=0
  8. SET volindex=0
  9. FOR %%I IN (1,2) DO SET "volume[%%I]=0"
  10.  
  11. FOR /L %%I IN (1 1 12) DO (
  12.   FOR /F "usebackq" %%V IN (`smartctl -x -d areca^,%%I/2 /dev/arcmsr0 ^| grep ^
  13. -e "read:" -e "write:" ^| tr -s " " ^| cut -d " " -f7 ^| cut -d "," -f1`) DO (
  14.     SET /A volindex += 1
  15.     SET "volume[!volindex!]=%%V"
  16.   )
  17.   SET /A totalreadvolume += !volume[1]!
  18.   SET /A totalwritevolume += !volume[2]!
  19.   SET volindex=0
  20. )
  21. SET /A totalreadTiB = "%totalreadvolume% / 1024"
  22. SET /A totalwriteTiB = "%totalwritevolume% / 1024"
  23. SET /A parityreadTiB = "%totalreadTiB% / 6"
  24. SET /A paritywriteTiB = "%totalreadTiB% / 6"
  25. SET /A totaluserreadTiB = "%totalreadTiB% - %parityreadTiB%"
  26. SET /A totaluserwriteTiB = "%totalwriteTiB% - %paritywriteTiB%"
  27.  
  28.  
  29. ECHO ^<b^>Areca ARC-1883ix-12 RAID controller status:^<br^> > "Z:\web\xin\raid6^
  30. stats\raid6stats_temp.txt.html"
  31.  
  32. ECHO ==========================================^</b^>^<br^>^<br^> >> "Z:\web\xi^
  33. n\raid6stats\raid6stats_temp.txt.html"
  34. "C:\Program Files (x86)\system\ArcCLI\cli.exe" sys info | grep -e "Main Process^
  35. or" -e CPU -e "System Memory" -e "Controller Name" | sed -e "s/1200MHz/PowerPC^
  36.  476 dual-core, 1200MHz/g" -e "s/SCache\sSize\s\s/L2 Cache Size/g" -e "s/\s/\&^
  37. nbsp;/g" -e "s/^/\&nbsp;\&nbsp;/g" -e "s/$/<br>/g" >> "Z:\web\xin\raid6stats\r^
  38. aid6stats_temp.txt.html"
  39.  
  40. "C:\Program Files (x86)\system\ArcCLI\cli.exe" hw info | grep -e "CPU Temperatu^
  41. re" -e "Controller Temp" -e "CPU Fan" -e "12V" -e "5V" -e "3\.3V" -e "IO Volta^
  42. ge" -e "DDR3" -e "CPU VCore" -e "Ethernet" -e "Battery Status" -e "Chip Temp" ^
  43. | sed -e "s/\sC$/\&deg;C/g" -e "s/\sV$/V/g" -e "s/\sRPM$/rpm/g" -e "s/\x25/F/g^
  44. " -e"s/^\s\s//g" -e "s/\s:\s/   : /g" -e "s/Chip\sTemp\s\s\s\s\s\s\s\s\s/SAS E^
  45. xpander Temp\./g" -e "s/\s/\&nbsp;/g" -e "s/^/\&nbsp;\&nbsp;/g" -e "s/$/<br>/g^
  46. " >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  47.  
  48. ECHO ^<br^>^<br^>^<br^>^<br^>^<b^>RAID ^&amp; volume set status:^<br^> >> "Z:\w^
  49. eb\xin\raid6stats\raid6stats_temp.txt.html"
  50. ECHO ========================^</b^>^<br^>^<br^> >> "Z:\web\xin\raid6stats\raid6^
  51. stats_temp.txt.html"
  52.  
  53. ECHO ^&nbsp;^&nbsp;RAID set:^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.tx^
  54. t.html"
  55. ECHO ^&nbsp;^&nbsp;--------^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt^
  56. .html"
  57. ECHO ^&nbsp;^&nbsp;^&nbsp;^&nbsp;#^&nbsp;Name^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp^
  58. ;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^
  59. ^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;Disks^&nbsp;TotalCap^&nbsp;^&n^
  60. bsp;^&nbsp;FreeCap^&nbsp;MinDiskCap^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&^
  61. nbsp;^&nbsp;^&nbsp;State^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.ht^
  62. ml"
  63.  
  64. "C:\Program Files (x86)\system\ArcCLI\cli.exe" rsf info | grep -e Taranis | sed^
  65.  -e "s/\sTaranis\sRAID-6\sC/Taranis RAID-6 CryptoArray/g" -e "s/^\s/  /g" -e "s^
  66. /\s/\&nbsp;/g" -e "s/^/\&nbsp;\&nbsp;/g" -e "s/$/<br>/g" >> "Z:\web\xin\raid6st^
  67. ats\raid6stats_temp.txt.html"
  68.  
  69. ECHO ^<br^>^&nbsp;^&nbsp;Volume set:^<br^> >> "Z:\web\xin\raid6stats\raid6stats^
  70. _temp.txt.html"
  71. ECHO ^&nbsp;^&nbsp;----------^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.t^
  72. xt.html"
  73. ECHO ^&nbsp;^&nbsp;^&nbsp;^&nbsp;#^&nbsp;Name^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp^
  74. ;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^
  75. ^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;^&nbsp;UsableCap^&nbsp;^
  76. Ch/Id/Lun^&nbsp;^&nbsp;State^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.tx^
  77. t.html"
  78.  
  79. "C:\Program Files (x86)\system\ArcCLI\cli.exe" vsf info | grep -e Taranis | sed^
  80.  -e "s/Taranis\sRAID-6\sC\sTaranis\sRAID-6\sCRaid6/Taranis RAID-6 CryptoArray/g^
  81. " -e "s/\s/\&nbsp;/g" -e "s/^/\&nbsp;\&nbsp;/g" -e "s/$/<br>/g" >> "Z:\web\xin\^
  82. raid6stats\raid6stats_temp.txt.html"
  83.  
  84. ECHO ^<br^>^<br^>^<br^>^<b^>Global array S.M.A.R.T. information:^<br^> >> "Z:\w^
  85. eb\xin\raid6stats\raid6stats_temp.txt.html"
  86. ECHO ===================================^</b^>^<br^>^<br^> >> "Z:\web\xin\raid6^
  87. stats\raid6stats_temp.txt.html"
  88. ECHO ^&nbsp;^&nbsp;Total data read from array (raw, with N+P parity data) :^
  89.  ~%totalreadTiB% TiB^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  90. ECHO ^&nbsp;^&nbsp;Total data written to array (raw, with N+P parity data):^
  91.  ~%totalwriteTiB% TiB^<br^>^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt^
  92. .html"
  93. ECHO ^&nbsp;^&nbsp;Total data read from array (user data) : ~%totaluserreadTiB%^
  94.  TiB^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  95. ECHO ^&nbsp;^&nbsp;Total data written to array (user data):^
  96.  ~%totaluserwriteTiB% TiB^<br^>^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp^
  97. .txt.html"
  98. ECHO ^&nbsp;^&nbsp;^<em^>^<span style="font-size: 8pt;"^>Note: Parity data is i^
  99. ncluded in raw total reads, because the controller is^<br^> >> "Z:\web\xin\raid^
  100. 6stats\raid6stats_temp.txt.html"
  101. ECHO ^&nbsp;^&nbsp;configured to read ^&amp; discard parity data to minimize re^
  102. -seeks and optimize^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  103. ECHO ^&nbsp;^&nbsp;for sequential reading performance.^</span^>^</em^>^<br^> >>^
  104.  "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  105.  
  106. ECHO ^<br^>^<br^>^<br^>^<b^>Per-Disk S.M.A.R.T. information:^<br^> >> "Z:\web\x^
  107. in\raid6stats\raid6stats_temp.txt.html"
  108. ECHO ===============================^</b^>^<br^> >> "Z:\web\xin\raid6stats\raid^
  109. 6stats_temp.txt.html"
  110.  
  111. FOR /L %%M IN (1 1 12) DO (
  112.   ECHO ^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  113.   ECHO ^&nbsp;^&nbsp;Disk %%M:^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.^
  114. txt.html"
  115.   ECHO ^&nbsp;^&nbsp;-------^<br^> >> "Z:\web\xin\raid6stats\raid6stats_temp.tx^
  116. t.html"
  117.   smartctl -a -d areca,%%M/2 /dev/arcmsr0 | grep -e "Vendor[:space:]" -e Produc^
  118. t -e "User Capacity" -e "Rotation Rate" -e "Transport Protocol" -e "Current Dri^
  119. ve Temperature" -e "Accumulated start-stop cycles" -e "Accumulated load-unload ^
  120. cycles" -e "Elements in grown defect list" | sed -e "s/\sC$/\&deg;C/g" -e "s/Ve^
  121. ndor:\s\s\s\s\s\s\s\s\s\s\s\s\s\s\s/Vendor                        : /g" -e "s/P^
  122. roduct:\s\s\s\s\s\s\s\s\s\s\s\s\s\s/Product                       : /g" -e "s/U^
  123. ser\sCapacity:\s\s\s\s\s\s\s\s/User capacity                 : /g" -e "s/Rotati^
  124. on\sRate:\s\s\s\s\s\s\s\s/Rotation rate                 : /g" -e "s/Current\sDr^
  125. ive\sTemperature:\s\s\s\s\s/Current drive temperature     : /g" -e "s/start-sto^
  126. p\scycles:\s\s/start-stop cycles : /g" -e "s/grown\sdefect\slist:\s/grown defec^
  127. t list : /g" -e "s/load-unload\scycles:\s\s/load-unload cycles: /g" -e "s/\s/\&^
  128. nbsp;/g" -e "s/^/\&nbsp;\&nbsp;\&nbsp;\&nbsp;/g" -e "s/$/<br>/g" >> "Z:\web\xin^
  129. \raid6stats\raid6stats_temp.txt.html"
  130.  
  131.   smartctl -H -d areca,%%M/2 /dev/arcmsr0 | grep Health | sed -e "s/Status:/Sta^
  132. tus           :/g" -e "s/\s/\&nbsp;/g" -e "s/^/\&nbsp;\&nbsp;\&nbsp;\&nbsp;/g" ^
  133. -e "s/$/<br>/g" >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  134. )
  135.  
  136. ECHO ^<br^>^<br^>^<br^>^<br^>^<em^>Last update: >> "Z:\web\xin\raid6stats\raid6^
  137. stats_temp.txt.html"
  138. DATE /T >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  139. ECHO (DD.MM.YYYY),^  >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  140. TIME /T >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  141. ECHO ^</em^> >> "Z:\web\xin\raid6stats\raid6stats_temp.txt.html"
  142.  
  143. COPY /b /Y "Z:\web\xin\raid6stats\header.html" + "Z:\web\xin\raid6stats\raid6st^
  144. ats_temp.txt.html" + "Z:\web\xin\raid6stats\footer.html" "Z:\web\xin\raid6stats^
  145. \raid6stats.html"
  146.  
  147. ENDLOCAL

Guess you can’t even read that at all? Yeah, because it’s shit! Also, Batch is one hell of an obscure language, seriously. Doing nested loops (or any loops actually) can break your variable expansion inside the loop body (and thus: your entire program) when doing assignments in no time. Arithmetics look ugly as hell. And pushing command outputs into variables absolutely requires a FOR /F loop, there just is no other way at all. Backticks? Yeah, keep dreaming.

The whole syntax is just plain painful. It really hurts writing that code, and that’s just a simple program. I don’t even wanna see any seriously complex Batch code, ever.

But well, a load of GNU tools are helping Batch walk through this left and right, and it works, so I’m just gonna leave it alone. When a migration to a UNIX-style OS comes, I’ll just have to rewrite it using Bash or something.

Well, so far so good I guess…

Edit: Seems I forgot to post some of the benchmarks here. In the meantime, we have a completed verify process with information about how long it takes on this 54.5TiB array, as well as those missing sequential r/w benchmarks and another photo of the system, all in [part 4¾] (it’s just a minor update)!

Aug 102015
 

S.H.I.E.L.D. Helicarrier logoI know I haven’t posted anything useful recently, or anything at all actually. Reason being that I’ve been basically wallowing in laziness for the last two weeks, using my vacation for barely anything but binge-watching Anime (a seriously vast, clearly unhealthy amount of it, as I have to admit), games and… nothing else basically. I did get a present for my upcoming birthday though, and somehow I managed to complete at least that part.

I’ve been a fan of the S.H.I.E.L.D. helicarrier from the Marvel superhero universes since I was maybe 10 years old or so. I didn’t have a lot of Marvel comics, maybe a dozen or so, but there was one Hulk comic featuring the flying carrier, and I was just in love immediately. Needless to say that I was totally crazy about the [vessel appearing]Small YouTube logo surprisingly in the Marvel [The Avengers]IMDb logo movie.

So I’ve been eyeing the massive Lego version of it for some time, but ultimately, I got ~50% of it as a present, and payed for the rest myself. It would’ve been to much either way otherwise, as it’s priced at a steep 349.99€ and $349.99 at [Lego]. You can also forget other sellers here in central Europe, as they’re all more expensive than the Lego shop itself.

Now I don’t have build log pictures for you (my cousin made some with her phone while assembling the carrier, but I don’t have those photos yet), but here is the final >80cm piece, fully assembled, lacking only the battery pack and motor for the powered rotors and lighting effects. Maybe I’ll get that later:

At 2996 pieces and a length of >80cm with a width of >45cm “small” isn’t the right word to describe this model, which is a mix of regular Lego and Lego Technic. Unfortunately the manual is monolithic, which means that only one person can build at the same time, and other persons can only assist by checking for errors and looking for parts the builder needs. Since we tend to build models in teams of 2-3 people, I’d prefer the way some larger Lego Technic models had this solved with 3-4 individual manuals, where multiple builders could work at a table at the same time. That’s much more fun. The way we had to build it, it took roughly 14 manhours to complete, counting only the builders work (mostly me, partly my cousin).

Anyway, let’s go for the close-ups:

While the microfigures are quite nice considering their scale, the Quinjets are definitely far too large, but I can see how downscaling them further would’ve been hard, and I’m ok with that. Besides the microfigures Lego also added a few normal ones for the display stand:

Display stand

Display stand

From left to right that’s Hawkeye, Maria Hill, Captain America, Black Widow and finally, Nick Fury. I had hoped for the Iron Man as well, but we get Mr. Stark only as a microfigure.

And that was probably the only productive thing I got done in my entire vacation. :roll:

Jun 282015
 

Ulfs Knife logoSome days in June you’d find me in the castle of Oberkapfenberg, attending the [Medieval Festival]Austrian Flag there. Lots of Jesters, Shows, medieval music, swordplay, archery, old foods & drinks, the castle falconrys’ birds of prey show and craftsmen can be found there, plus a ton of cosplay of course. Now while I can’t provide any good photographs of that, I’d like to show you something I got from [Ulf fum Oachberg]Austrian Flag, a medieval blacksmith who attends the festivities every year I’ve been there so far. Well actually it’s more like Umlüx talked me into writing this for pressuring him to write something about that [Böker Cronidur-30 knife] he got back then. ;)

I always wanted to buy something from the guy, so we approached the smithy with all its swords and polearms and other metal goods, and talked to him for what was probably half an hour or so. He’ll take the time to explain his forging techniques, his equipment and the steels used in his blades. You can also watch him work right on-site, as he has fire, bellows, hammer & anvil all with him there. One knife however immediately caught my eye, and after a bit of bartering (which I suck at, but yeah), I got it for 164€, all hand-forged and hand-carved (click to enlarge the pictures):

As you may recognize from the blades’ banding, this is not monosteel, but a damascene-style compound steel. It’s made from multiple folded steels which do contain carbon too, making this blade not stainless, but giving it a high resistance to shattering and a supposedly very resilient edge. The handle is made from the horn of a roebuck, featuring a hand-carved lizard ornament. A piece of what I think is some harder leather (I forgot to ask about it) sits on top of the handle, where the blade begins to show itself.

Let’s take a closer look:

Now, Ulf said that while this knife is expensive, I’ll likely never need another one for the rest of my life. I was a bit worried about the carbon in the steel, so I asked him about whether the blade would need any special kind of treatment. I did remember that Japanese carbon steel blades were usually treated with clove oil for some coating on the metal. Ulf however said, that cutting a few slices off a chunk of bacon or some dried sausage every now and then is sufficient. Cleaning the knife with a piece of dry cloth afterwards shall still leave enough fat on the metal to form a nice, protective layer.

And, as grandmother always says (and Ulf too), if you do need to wash it, never let hot water touch a sharp blade, and dry it immediately after washing. Well that much’s common sense I’d say.

Still, can I judge its resilience and longevity? No, of course not. I know not nearly enough about knives and steels to do so. Let’s try to look at what I can; Believing Ulf the blacksmith, I’d like to think that this is some high quality Damascus steel right there. But what about the blades’ angle? Extremely sharp knives tend to have angles as low as 15-10° or even less, some even sharper knives might have hollow ground blades. Those tend to wear out really fast though. Let’s have a look:

The blade angle, measured at around 30°

The blade angle, measured at around 30°.

The blade can be measured at an almost exact 30° angle (yeah ok, it’s not a perfectly perspectively correct measurement, but well). Now it does get a bit narrower at the edge due to the sharpening, but not by much. My guess would be around 26-28° at the very edge. That should put it into a category of blades with very high durability and slightly less sharpness. Which is not bad in my book. Cutting dried meat almost feels like going through butter still, so there’s nothing to complain here:

Cutting things like this dried and very tasty boar sausage is exactly what this knife was made for!

Cutting things like this dried and very tasty boar sausage is exactly what this knife was made for!

What’s left now is a proper sheath. Since I don’t want to just buy some run-of-the mill one for this knife, I’ll likely go to our local shoemaker and tanner (Hell, we actually still got one in my town! Can you believe that?!) and get a custom-made one for it, which should be much more fitting than some mass-produced stuff.

I really wonder how this knife will handle 30 years down the road from now. I guess I just have to find out, it’s just going to take a bit of time. ;)

Feb 272015
 

Ren'Py on FreeBSD logo[1] While not exactly “games” in the common sense of the word, visual novels are often classified as such. I’d call them a crossover between novels/manga and games, much like interactive books with storyline branching options for kids (in case you know those). Most of these visual novels – all which I know at least – are based on the [Ren’Py] engine, which itself sits on top of the Python scripting language and I believe the [pygame] library in all cases. This makes the stuff great for portability, since Python exists on a wide variety of operating systems.  While traditionally a bit more at home on Linux/UNIX, it has long since been ported to Microsoft Windows too.

Now to the interesting part: While the portability has been recognized by the Ren’Py team and also the visual novel developers, what you’ll usually get is a Windows, a MacOS X and a Linux version. Then I thought: Why? If it’s just Python, I should be able to just copy it anywhere I like, as long as I have Python itself plus the necessary modules and the Ren’Py engine installed there. My target once more: FreeBSD 10.1 UNIX.

Ren'Py launcher

The Ren’Py SDK including the engine runtime can easily be installed on FreeBSD too. The complications don’t end there however.

It turned out to be harder than expected though. Ren’Py does have dependencies on certain system libraries (for video decoding, image processing, etc.) as well as on the Python and modules versions themselves. That’s why each and every Ren’Py novel is (partly unfortunately) distributed with the whole of Python, the Ren’Py engine itself and different system libraries pre-compiled and statically linked together. Naturally, those libraries are written in C/C++, so there you have your operating system dependency.

I tried this with the novel “Sunrider – Mask of Arcadius” first. Just directly copied out of my Steam folder on Windows, straight to CentOS 6 Linux (where it worked out of the box) and then to FreeBSD 10.1 UNIX. Now the game may use small binary executables to launch, like an EXE file on Windows, but all those do really is launch the packaged Python properly. For Linux, it’s launched just by a shell script:

expand/collapse source code
  1. #!/bin/sh
  2.  
  3. SCRIPT="$0"
  4.  
  5. # Resolve the chain of symlinks leading to this script.
  6. while [ -L "$SCRIPT" ] ; do
  7.     LINK=$(readlink "$SCRIPT")
  8.  
  9.     case "$LINK" in
  10.         /*)
  11.             SCRIPT="$LINK"
  12.             ;;
  13.         *)
  14.             SCRIPT="$(dirname "$SCRIPT")/$LINK"
  15.             ;;
  16.     esac
  17. done
  18.  
  19. # The directory containing this shell script - an absolute path.
  20. ROOT=$(dirname "$SCRIPT")
  21. ROOT=$(cd "$ROOT"; pwd)
  22.  
  23. # The name of this shell script without the .sh on the end.
  24. BASEFILE=$(basename "$SCRIPT" .sh)
  25.  
  26. if [ -z "$RENPY_PLATFORM" ] ; then
  27.     case "$(uname -s)-$(uname -m)" in
  28.         Darwin-*)
  29.             RENPY_PLATFORM="darwin-x86_64"
  30.             ROOT1="$ROOT/../Resources/autorun"
  31.             ROOT2="$ROOT/../../.."
  32.                         ;;
  33.         *-x86_64|amd64)
  34.             RENPY_PLATFORM="linux-x86_64"
  35.             ROOT1="$ROOT"
  36.             ROOT2="$ROOT"
  37.                         ;;
  38.         *-i*86)
  39.             RENPY_PLATFORM="linux-i686"
  40.             ROOT1="$ROOT"
  41.             ROOT2="$ROOT"
  42.             ;;
  43.         *)
  44.             echo "Ren'Py could not detect that platform it's running on. Please set"
  45.             echo "the RENPY_PLATFORM environment variable to one of \"linux-i686\" or"
  46.             echo "\"linux-x86_64\", or \"darwin-x86_64\" and run this command again."
  47.             exit 1
  48.             ;;
  49.     esac
  50. fi
  51.  
  52.  
  53. for BASE in "$ROOT" "$ROOT1" "$ROOT2"; do
  54.     LIB="$BASE/lib/$RENPY_PLATFORM"
  55.                 if test -d "$LIB"; then
  56.                     break
  57.                 fi
  58. done
  59.  
  60. exec $RENPY_GDB "$LIB/$BASEFILE" $RENPY_PYARGS -EO "$BASE/$BASEFILE.py" "$@"

I decided to bypass that entirely. After installing the Ren’Py engine on FreeBSD system-wide by running # pkg install renpy, I entered the games directory and ran $ python ./SunriderMaskofArcadius.py. That works, but only out of sheer luck. I tried others, like Analogue: A Hate Story or Katawa Shoujo (warning: contains erotic content) with no such luck. The space-opera Sunrider however, no problem:

The Sunrider assault carrier takes off - on FreeBSD 10.1

The Sunrider assault carrier takes off – on FreeBSD 10.1 just as well as anywhere else

So: Why? The answer is: Because Python. Sunrider is one of the rarer gems that are written for a Ren’Py version new enough (6.18.1.670) to ship with Python 2.7. And that’s exactly the version that comes with FreeBSD 10.x, and also the version its renpy-6.18.3 package is built for. Most visual novels however were developed using older engines that come with a pre-built Python 2.6 or 2.5 for Windows/OSX/Linux.

Now I tried compiling Python 2.5 from source, which was successful. It doesn’t end there however, as you then also need to use Python 2.5 for Ren’Pys build system to compile its own platform-specific code too and to get the engine “linked” with Python 2.5. I used an older engine for that to ensure compatibility, but the fun still ends there very quickly, as I ran into severe dependency issues, some of which [this Debian bug report] also describes. One would need to build themselves an old libpng, old libav and god knows what else to be able to get the whole thing up and running. And just running old novels with Python 2.7? Ha! No:

expand/collapse source code
  1. Traceback (most recent call last):
  2.   File "./Katawa Shoujo.py", line 132, in 
  3.     renpy.bootstrap.bootstrap(renpy_base)
  4.   File "/usr/home/&lt;myuser&gt;/games/Katawa Shoujo-linux-x86/renpy/bootstrap.py", line 242, in bootstrap
  5.     renpy.import_all()
  6.   File "/usr/home/&lt;myuser&gt;/games/Katawa Shoujo-linux-x86/renpy/__init__.py", line 61, in import_all
  7.     import renpy.display.scale # Must be before module.
  8.   File "/usr/home/&lt;myuser&gt;/games/Katawa Shoujo-linux-x86/renpy/display/scale.py", line 31, in 
  9.     import _renpy_font
  10. ImportError: No module named _renpy_font
  11. Exception TypeError: "poll() got an unexpected keyword argument '_deadstate'" in &gt; ignored
  12. Traceback (most recent call last):
  13.   File "./Katawa Shoujo.py", line 132, in 
  14.     renpy.bootstrap.bootstrap(renpy_base)
  15.   File "/usr/home/&lt;myuser&gt;/games/Katawa Shoujo-linux-x86/renpy/bootstrap.py", line 170, in bootstrap
  16.     renpy.display.presplash.show(options.presplash)
  17.   File "/usr/home/&lt;myuser&gt;/games/Katawa Shoujo-linux-x86/renpy/display/presplash.py", line 101, in show
  18.     sys.stdout.flush()
  19. IOError: [Errno 32] Broken pipe

Ren’Py for Python 2.5 cannot run on 2.7, some module import issues. And 2.7 trying to run a novel based on Python 2.6 gives you this:

expand/collapse source code
  1. Traceback (most recent call last):
  2.   File "./Analogue.py", line 144, in 
  3.     main()
  4.   File "./Analogue.py", line 141, in main
  5.     renpy.bootstrap.bootstrap(renpy_base)
  6.   File "/usr/home/&lt;myuser&gt;/games/Analogue A Hate Story/renpy/bootstrap.py", line 279, in bootstrap
  7.     renpy.import_all()
  8.   File "/usr/home/&lt;myuser&gt;/games/Analogue A Hate Story/renpy/__init__.py", line 139, in import_all
  9.     import renpy.display.render # Most display stuff depends on this. @UnresolvedImport
  10.   File "render.pyx", line 324, in init renpy.display.render (gen/renpy.display.render.c:22252)
  11. AttributeError: 'module' object has no attribute 'Sentinel'

I stopped there.

I’m not saying it can’t be done, but compiling myself all those dependencies? I didn’t want to go that far. Who knows what kind of ever-growing dependency tree horror awaits there.

So yeah, Ren’Py visual novels can be played on FreeBSD 10.x, but you’ll need to make absolutely sure that it’s a game coming with a new enough version of Python. If you only have FreeBSD systems, there are still several ways to verify this by running the Windows binary using wine, the Linux binary using FreeBSDs linuxulator, or by checking the strings on the Linux binary. While sitting in the games main directory, try these (you may need to adjust the paths accordingly):

  • $ wine ./lib/windows-i686/python.exe --version | grep 2\.7 should give back some insignificant error messages and a string like “Pyton 2.7.3”. Requires wine obviously. Don’t get anything? Change the grep part to look for 2\.6 or 2\.5 instead.
  • $ LD_LIBRARY_PATH="./lib/linux-i686/" ./lib/linux-i686/python --version should give back a string like “Python 2.7.3”. Requires linuxulator.
  • strings ./lib/linux-i686/python | grep 2\.7 should give back a Python library the binary was linked against, like libpython2.7.so.1.0. No results? Change the grep part to look for 2\.6 or 2\.5 instead.

If the Python shipped with the novel in question is too old, I’d say just leave it or run it on Windows/OSX/Linux instead. Or you can play hero and try to build yourself all the required libraries, then compile and link an older Python, pygame module and Ren’Py engine against them. Would love to see how hard it’d really be. ;)

[1] Original drawing is © by ASK (Pixiv profile).【PFFK】琉璃, Pixiv Fantasia: Fallen Kings series. Altered and used with express permission.