<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Fan's Secret Garden]]></title><description><![CDATA[Wisdom aggregated.]]></description><link>https://blog.amayume.net/</link><image><url>https://blog.amayume.net/favicon.png</url><title>Fan&apos;s Secret Garden</title><link>https://blog.amayume.net/</link></image><generator>Ghost 2.38</generator><lastBuildDate>Tue, 10 Mar 2026 02:45:22 GMT</lastBuildDate><atom:link href="https://blog.amayume.net/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Connect to the Georgia Tech VPN with OpenConnect and NetworkManager]]></title><description><![CDATA[<p>First let's assume you have an Arch Linux based system.</p><ol><li> <code>sudo pacman -S openconnect networkmanager-openconnect</code> </li><li>Gateway: anyc.vpn.gatech.edu</li><li>IPv4-&gt;Routes-&gt;Only use for resources on this network</li><li><code>sudo setcap cap_net_admin+ep /usr/bin/openconnect</code></li><li><code>sudo nano /usr/share/dbus-1/system.d/nm-openconnect-service.conf</code>, edit</li></ol>]]></description><link>https://blog.amayume.net/connect-to-the-georgia-tech-vpn-with-openconnect-and-networkmanager/</link><guid isPermaLink="false">5d570c787d9d58729d7c5992</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Fri, 16 Aug 2019 20:08:12 GMT</pubDate><content:encoded><![CDATA[<p>First let's assume you have an Arch Linux based system.</p><ol><li> <code>sudo pacman -S openconnect networkmanager-openconnect</code> </li><li>Gateway: anyc.vpn.gatech.edu</li><li>IPv4-&gt;Routes-&gt;Only use for resources on this network</li><li><code>sudo setcap cap_net_admin+ep /usr/bin/openconnect</code></li><li><code>sudo nano /usr/share/dbus-1/system.d/nm-openconnect-service.conf</code>, edit the last two <code>deny</code>s into <code>allow</code>.</li><li>Profit!</li></ol>]]></content:encoded></item><item><title><![CDATA[Write-up for the Google CTF 2019]]></title><description><![CDATA[<h1 id="introduction">Introduction</h1><!--kg-card-begin: markdown--><p>This is my 2<sup>nd</sup> time participating in a CTF contest. This time I paired up with awesome people from TUNA and THU CSTA in the Tea Deliverers team. These guys are like superheroes when it comes to problem-solving!</p>
<p>Finally I was only able to fully solve one problem:</p>]]></description><link>https://blog.amayume.net/write/</link><guid isPermaLink="false">5d11ac627d9d58729d7c5883</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Tue, 25 Jun 2019 05:56:38 GMT</pubDate><content:encoded><![CDATA[<h1 id="introduction">Introduction</h1><!--kg-card-begin: markdown--><p>This is my 2<sup>nd</sup> time participating in a CTF contest. This time I paired up with awesome people from TUNA and THU CSTA in the Tea Deliverers team. These guys are like superheroes when it comes to problem-solving!</p>
<p>Finally I was only able to fully solve one problem: poly. I'll show the problem and my solution below.</p>
<!--kg-card-end: markdown--><h1 id="problem">Problem</h1><p><code>nc poly.ctfcompetition.com 1337</code></p><!--kg-card-begin: markdown--><p>It will present this banner:</p>
<pre><code>Give me a bios image that I can load into qemu that will send
&quot;Pull_that_lever1&quot;to 169.254.169.254:80 over tcp. The server can
be delayed a bit, so your payload should retry connecting. And by
the way, it has to work both in x86_64 and on a qemu arm virt-2.8
machine, all in under 300 seconds

To make your life easier, here are the commands the server will run:

/usr/bin/qemu-system-arm -nographic -machine virt-2.8 -net nic -net &quot;user,restrict=on,net=169.254.0.0/16,host=169.254.169.253,guestfwd=tcp:169.254.169.254:80-cmd:netcat 127.0.0.1 $random_port&quot; -bios $tmpfile

/usr/bin/qemu-system-x86_64 -nographic -net nic -net &quot;user,restrict=on,net=169.254.0.0/16,host=169.254.169.253,guestfwd=tcp:169.254.169.254:80-cmd:netcat 127.0.0.1 $random_port&quot; -bios $tmpfile

Boot image (base64 encoded, limit 3MiB encoded):
</code></pre>
<p>It is obvious that it wants you to construct a polyglot that can run<br>
both on ARM and x86. At first thought one would think of using some<br>
&quot;dual-use&quot; instructions to construct a &quot;loader&quot; and jump to payloads<br>
for different platforms. This strategy works for shellcodes beautifully, however, this will not work because we are constructing a BIOS image.</p>
<!--kg-card-end: markdown--><h2 id="bios-loading-process-on-x86-and-arm">BIOS Loading Process on x86 and ARM </h2><!--kg-card-begin: markdown--><p>Let there be some background information on the BIOS loading process of an x86 machine.<br>
We all know that the BIOS is actually stored on some flash memory chip on an actual motherboard. On x86 this memory is mapped to the <code>0xF0000</code> to <code>0x100000</code> range. The key point is that, the system will jump to <code>0x100000</code> after initialization. And this address is actually the end of the BIOS ROM image.</p>
<pre><code>0x00000 .. 0xA0000      DOS Memory Area       RAM
0xA0000 .. 0xC0000      Video Memory          Device Memory
0xC0000 .. 0xE0000      ISA Extension ROM     ROM
0xE0000 .. 0xF0000      BIOS Extension ROM    ROM
0xF0000 .. 0x100000     BIOS Area             ROM
(Quoted from QEMU Wiki)
</code></pre>
<p>However, on ARM machines, this is not the case. The address mapping is largely vender-dependent while for our case (The QEMU virt 2.7 machine), Execution starts from <code>0x00000000</code> and this is mapped to the begin of the ROM image. Voila! We just need to concat the two files and our polyglot is done!</p>
<!--kg-card-end: markdown--><h2 id="solution">Solution</h2><!--kg-card-begin: markdown--><p>Now the problem becomes &quot;how to create the payloads for ARM and x86?&quot;. Well, there is a project called <em>U-Boot</em> (<em>Das U-Boot</em>) that is known by every embedded developer, which is a bootloader for Linux (and others) for almost all platforms. We just compile U-Boot on both platforms and concat them! Also, your final payload must be padded to multiples of 65536 bytes.</p>
<p>And now we have the following snippet:</p>
<pre><code class="language-asm">; multi-arch u-boot

; compile with nasm
bits 32
_start:
    incbin &quot;../u-boot/u-boot.bin&quot;
    times 23812 db 0x90 ; This is calculated padding
    incbin &quot;../u-boot-x86/u-boot.rom&quot;
</code></pre>
<p>Now the payload will already be running on both platforms. Meanwhile, another problem arises that the two machines have different Ethernet hardware: <code>e1000</code> for x86 and <code>virtio-net</code> for ARM. Fortunately, U-Boot has built-in support for both cards. We just need to compile it and add some code for actually loading it with the U-Boot Driver Model (DM).</p>
<pre><code class="language-c">int do_demo_net(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct udevice *dev;
	int i, ret;

	puts(&quot;VIRTIO uclass entries:\n&quot;);
	int devnum = 0;
	struct udevice *net_dev;
	ret = uclass_get_device(UCLASS_VIRTIO, devnum, &amp;net_dev);
	printf(&quot;getting devices %d\n&quot;, ret);
	for (i = 0, ret = uclass_first_device(UCLASS_VIRTIO, &amp;dev);
	     dev;
	     ret = uclass_next_device(&amp;dev)) {
		printf(&quot;entry %d - instance %08x, ops %08x, platdata %08x\n&quot;,
		       i++, (uint)map_to_sysmem(dev),
		       (uint)map_to_sysmem(dev-&gt;driver-&gt;ops),
		       (uint)map_to_sysmem(dev_get_platdata(dev)));
	}

	if (ret == 0) {
		ret = uclass_get_device(UCLASS_ETH, devnum, &amp;net_dev);

		printf(&quot;getting devices %d\n&quot;, ret);
		for (i = 0, ret = uclass_first_device(UCLASS_ETH, &amp;dev);
			dev;
			ret = uclass_next_device(&amp;dev)) {
			printf(&quot;entry %d - instance %08x, ops %08x, platdata %08x\n&quot;,
				i++, (uint)map_to_sysmem(dev),
				(uint)map_to_sysmem(dev-&gt;driver-&gt;ops),
				(uint)map_to_sysmem(dev_get_platdata(dev)));
		}
	}
	return cmd_process_error(cmdtp, ret);
}
</code></pre>
<p>Seems easy, right? It comes with a big surprise-the U-Boot network stack does not support TCP! I patched it against a TCP <a href="https://lists.denx.de/pipermail/u-boot/2018-March/323699.html">patch</a> that I found from the U-Boot mailing list, and hacked it up to send the challenge and receive the reply(Turns out to be unnecessary).</p>
<p>Now we just pwn the server:</p>
<pre><code class="language-python">from pwn import *

#context.log_level = 'debug'
context.proxy = (socks.SOCKS5, 'localhost', 1080) 
p = remote(&quot;poly.ctfcompetition.com&quot;, 1337)

p.recvuntil(&quot;(base64 encoded, limit 3MiB encoded):&quot;)
p.send(file(&quot;./noptest.b64&quot;, &quot;rb&quot;).read())
p.interactive()
exit()
</code></pre>
<p>And here be the flag!</p>
<!--kg-card-end: markdown--><h1 id="p-s-acknowledgment">P.S. &amp; Acknowledgment</h1><p>Really exciting competition and a nice learning experience for a <em>菜鸡</em> (newbie) like me.</p><p>The patched U-Boot with TCP can be found at <a href="https://github.com/ProfFan/u-boot">https://github.com/ProfFan/u-boot</a>.<br>Check the commit logs to learn how to use it.</p><p>The author would like to thank:</p><ul><li>The U-Boot authors :)</li><li>The patch author Duncan Hare for the TCP Patch</li><li>@riatre for his expertise (waaaaaaay above me XD)</li><li>@iromise for inviting me to the team</li><li>TUNA and all people from THU CSTA</li></ul>]]></content:encoded></item><item><title><![CDATA[Hacking the RIGOL MSO1104Z]]></title><description><![CDATA[<p>The RIGOL MSO series have a different encryption than the DSOs. Thus you need a JTAG cable to dump the memory and extract the keys.</p><h2 id="ts-jr">TS;JR</h2><p>Basically you can just follow the steps in https://www.youtube.com/watch?v=OvcGn_ScG5w</p><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/OvcGn_ScG5w?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><p>However, at the step when you need</p>]]></description><link>https://blog.amayume.net/hacking-the-rigol-mso1104z/</link><guid isPermaLink="false">5cf8764a7d9d58729d7c582b</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Thu, 06 Jun 2019 02:24:36 GMT</pubDate><content:encoded><![CDATA[<p>The RIGOL MSO series have a different encryption than the DSOs. Thus you need a JTAG cable to dump the memory and extract the keys.</p><h2 id="ts-jr">TS;JR</h2><p>Basically you can just follow the steps in https://www.youtube.com/watch?v=OvcGn_ScG5w</p><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/OvcGn_ScG5w?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><p>However, at the step when you need to generate the keys with <code>./rigup license [keyfile] 0x1C0FF</code>, do not just run the command. Run <code>cat [keyfile]</code>, and compare with the device serial you obtained by SCPI IDN command. You will discover that the serial number is wrong.</p><!--kg-card-begin: markdown--><pre><code>rigup scan - Version 0.4.1

        Hacked up for MSO1000Z(-S) rmd79, 0ff eevblog.com

RC5KEY1:        9BBBBBBBBBBBBBBBBBBBBBBBBBFAD3E6
RC5KEY2:        8BBBBBBBBBBBBBBBBBBBBBBBBB0A699C
XXTEAKEY:       8BBBBBBBBBBBBBBBBBBBBBBBBB24850B
PUBKEY:         00XXXXXXXXXXXXXX
PRIVKEY:        00XXXXXXXXXXXXXX
SERIAL:         DS1ZA203000000 [ This is WRONG ]
</code></pre>
<!--kg-card-end: markdown--><p>Replace the serial number with the one you got using SCPI <code>*IDN?</code>.</p><p>Now you can generate the license with <code>./rigup [keyfile] mso1100z.txt 0x1C0FF</code>.</p><p>And install with SCPI <code>:SYSTem:OPTion:INSTall Y4HHHHW66SW9HHHHMU75BBBBBBBB</code>.</p><p><em>Voilà!</em> Now you have a scope with all options unlocked.</p>]]></content:encoded></item><item><title><![CDATA[DIY Smartcard Authentication with SmartPGP]]></title><description><![CDATA[<p><strong>SECURITY DISCLAIMER: THE AUTHOR DOES IMPLY OR ENDORSE THAT THE PROCEDURES DEMONSTRATED IN THIS ARTICLE ARE SECURE AGAINS ATTACKS!</strong> Please do your own research whenever it comes to the security of your system!</p><h3 id="what-is-a-smartcard">What is a Smartcard</h3><p>A smartcard is a portable device with a MCU (usually security-enhanced MCU) that</p>]]></description><link>https://blog.amayume.net/diy-smartcard-authentication-with-smartpgp/</link><guid isPermaLink="false">5cc11d89cf304c0f83c87f17</guid><category><![CDATA[JavaCard]]></category><category><![CDATA[Authentication]]></category><category><![CDATA[SmartCard]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Thu, 25 Apr 2019 05:28:55 GMT</pubDate><content:encoded><![CDATA[<p><strong>SECURITY DISCLAIMER: THE AUTHOR DOES IMPLY OR ENDORSE THAT THE PROCEDURES DEMONSTRATED IN THIS ARTICLE ARE SECURE AGAINS ATTACKS!</strong> Please do your own research whenever it comes to the security of your system!</p><h3 id="what-is-a-smartcard">What is a Smartcard</h3><p>A smartcard is a portable device with a MCU (usually security-enhanced MCU) that can run programs stored in its secure memory, for example cryptographic algorithms. In other words, a smartcard is a "secure world" where you can store sensitive information, which systems outside of the card can use the information for authentication and encryption without revealing the secret stored.</p><h3 id="what-will-you-need">What will you need</h3><!--kg-card-begin: markdown--><ul>
<li>A Smartcard
<ul>
<li>JavaCard-based (Mine is JC30M48CR)</li>
<li>A guide can be found <a href="https://github.com/martinpaljak/GlobalPlatformPro/tree/master/docs/JavaCardBuyersGuide">here</a></li>
</ul>
</li>
<li>A Smartcard Reader
<ul>
<li>Any standard smartcard reader</li>
<li>Rule of thumb is to buy those can read SIM cards</li>
<li>Mine is Rocketek RT-SCR2 (Alcor Micro AU9560)</li>
</ul>
</li>
<li>Latest GnuPG (GPG)</li>
</ul>
<!--kg-card-end: markdown--><p>Great thanks to Fan Dang for kindly providing me with a card, you can visit his blog <a href="https://dang.fan/en/">here</a>.</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.amayume.net/content/images/2019/04/photo_2019-04-25_11-08-49.jpg" class="kg-image"><figcaption>Smartcard with Reader</figcaption></figure><!--kg-card-end: image--><h3 id="preparing-the-card">Preparing the Card</h3><p>First you need to install the following packages:</p><p><code>sudo pacman -S ant maven pcsclite pcsc-tools</code></p><p>Also make sure that you have OpenJDK 10 installed and selected with <code>archlinux-java set java-10-openjdk</code></p><p>And install <a href="https://github.com/martinpaljak/GlobalPlatformPro">GlobalPlatformPro</a> by Martin Paljak:</p><!--kg-card-begin: markdown--><pre><code class="language-bash">git clone https://github.com/martinpaljak/GlobalPlatformPro.git
cd GlobalPlatformPro
mvn package &amp;&amp; ant
</code></pre>
<p>Now you can peek into your card with:</p>
<pre><code class="language-bash">$ java -jar target/gp.jar -i
</code></pre>
<pre><code>GlobalPlatformPro 19.01.22-3-g05a90c2
Running on Linux 5.0.9-arch1-1-ARCH amd64, Java 10.0.2 by Oracle Corporation
Reader: Alcor Micro AU9560 01 00
ATR: 3B90958011FE6A
More information about your card:
    http://smartcard-atr.appspot.com/parse?ATR=3B90958011FE6A

Card Data:
Card Capabilities:
Version: 255 (0xFF) ID:   1 (0x01) type: DES3 length:  16
Version: 255 (0xFF) ID:   2 (0x02) type: DES3 length:  16
Version: 255 (0xFF) ID:   3 (0x03) type: DES3 length:  16
Key version suggests factory keys
</code></pre>
<p>If you have multiple card readers, just add <code>-r &quot;Alcor Micro AU9560 01 00&quot;</code> to the command line (replace with your reader name).</p>
<pre><code class="language-bash">&gt; java -jar target/gp.jar -l
</code></pre>
<pre><code>Warning: no keys given, using default test key 404142434445464748494A4B4C4D4E4F
ISD: A000000003000000 (OP_READY)
     Privs:   SecurityDomain, CardLock, CardTerminate, CardReset, CVMManagement
</code></pre>
<p>Now you can see that we have an empty card with default keys. Notice that this key is <strong>just like the root password</strong> of your system, so <strong>PLEASE CHANGE IT TO A SECURE ONE</strong> before using it as a secure device!</p>
<!--kg-card-end: markdown--><p>Now let's change the card key. Notice that this key is necessary for doing any administrative task on the card, so please keep it stored somewhere <strong>SAFE</strong> and <strong>DO NOT FORGET</strong> it.</p><!--kg-card-begin: markdown--><p>First generate a true random secret with OpenSSL:</p>
<pre><code class="language-bash">openssl rand --hex 16 | awk '{print toupper($0)}' &gt; .card_secret
</code></pre>
<p>Now we can lock the card with this key:</p>
<pre><code class="language-bash">cat .card_secret | xargs java -jar target/gp.jar -r &quot;Alcor Micro AU9560 01 00&quot; -lock
</code></pre>
<pre><code>Warning: no keys given, using default test key 404142434445464748494A4B4C4D4E4F
Card locked with: D61B707C088B2F8829C7218FC839A664
Write this down, DO NOT FORGET/LOSE IT!
</code></pre>
<p><strong>FROM THE POINT YOU LOCKED YOUR CARD, ALL COMMANDS MUST TAKE THE ARGUMENT</strong> <code>-key {YOUR_SECRET}</code> <strong>OR YOU MAY BRICK YOUR CARD!</strong></p>
<!--kg-card-end: markdown--><p>However it is a great hassle if we need to add the key to our command each time, so we can first unlock the card, if we can remember to <strong>LOCK BEFORE USING THE CARD AS SECURE!</strong></p><!--kg-card-begin: markdown--><p>Now we unlock the card with:</p>
<pre><code class="language-bash">cat .card_secret | xargs java -jar target/gp.jar -r &quot;Alcor Micro AU9560 01 00&quot; -unlock -key
</code></pre>
<pre><code>Default type=DES3 bytes=404142434445464748494A4B4C4D4E4F kcv=8BAF47 set as master key for A000000003000000
</code></pre>
<p>Again, <strong>REMEMBER TO LOCK THE CARD BEFORE PRODUCTION!</strong></p>
<!--kg-card-end: markdown--><h3 id="building-smartpgp">Building SmartPGP</h3><!--kg-card-begin: markdown--><p><a href="https://github.com/ANSSI-FR/SmartPGP">SmartPGP</a> is an open-source implementation of the OpenPGP card specification, which works with GnuPG for secure signing and encryption.</p>
<!--kg-card-end: markdown--><p>To build SmartPGP, first clone the SmartPGP repository:</p><!--kg-card-begin: markdown--><pre><code class="language-bash">git clone https://github.com/ANSSI-FR/SmartPGP.git
git checkout javacard-3.0.1 # Only this version fits in 80K of memory
# If your card has larger Flash, just use the master branch
# If master fails to build, checkout this branch and retry
</code></pre>
<p>And add the JavaCard SDK:</p>
<pre><code class="language-bash">git submodule add https://github.com/martinpaljak/oracle_javacard_sdks sdks
</code></pre>
<p>Now you need to change the config parameters for SmartPGP.</p>
<p>First specify the SDK path with <code>jckit=</code> in <code>build.xml</code>:</p>
<pre><code class="language-xml">    &lt;javacard jckit=&quot;./sdks/jc304_kit&quot;&gt;
      &lt;cap output=&quot;SmartPGPApplet.cap&quot; sources=&quot;src&quot; aid=&quot;d27600012401&quot; version=&quot;1.0&quot;&gt;
        &lt;applet class=&quot;fr.anssi.smartpgp.SmartPGPApplet&quot; aid=&quot;d276000124010303AFAF000000000000&quot;/&gt;
      &lt;/cap&gt;
    &lt;/javacard&gt;
</code></pre>
<p>The original SmartPGP requires too much on-card RAM, which will lead to errors when loading the applet. To reduce the RAM usage, change <code>src/fr/anssi/smartpgp/Constants.java</code>:</p>
<pre><code class="language-java">    protected static final short INTERNAL_BUFFER_MAX_LENGTH =
        (short)0x300;
</code></pre>
<p>Now we can start building the applet by:</p>
<pre><code class="language-bash">ant
</code></pre>
<p>which will create the applet CAP file <code>SmartPGPApplet.cap</code>.</p>
<!--kg-card-end: markdown--><h3 id="installation-of-the-applet">Installation of the Applet</h3><!--kg-card-begin: markdown--><p>Now it is time to install the applet to your card:</p>
<pre><code class="language-bash">java -jar target/gp.jar -r &quot;Alcor Micro AU9560 01 00&quot; -install ../SmartPGP/SmartPGPApplet.cap
</code></pre>
<pre><code>Warning: no keys given, using default test key 404142434445464748494A4B4C4D4E4F
CAP loaded
</code></pre>
<p>If it shows</p>
<pre><code>Warning: no keys given, using default test key 404142434445464748494A4B4C4D4E4F
CAP loaded
INSTALL [for install and make selectable] failed: 0x6985 (Conditions of use not satisfied)
</code></pre>
<p>Your card do not have enough RAM and you need to decrease the <code>INTERNAL_BUFFER_MAX_LENGTH</code> parameter.</p>
<p>If it shows</p>
<pre><code>Warning: no keys given, using default test key 404142434445464748494A4B4C4D4E4F
Loading failed. Are you sure the CAP file (JC version, packages, sizes) is compatible with your card?
LOAD failed: 0x6A80 (Wrong data/incorrect values in data)
</code></pre>
<p>Your card do not support the JavaCard SDK version. Please try a lower version of the SDK, e.g. <code>jc304_sdk</code> if you are using <code>jc305u1_sdk</code> or higher.</p>
<p>Now you should <strong>LOCK YOUR CARD</strong> by:</p>
<pre><code class="language-bash">cat .card_secret | xargs java -jar target/gp.jar -r &quot;Alcor Micro AU9560 01 00&quot; -lock
</code></pre>
<!--kg-card-end: markdown--><h3 id="setting-up-the-card">Setting up the card</h3><p>Just follow the Debian wiki at <a href="https://wiki.debian.org/Smartcards/OpenPGP">https://wiki.debian.org/Smartcards/OpenPGP</a>.</p><p>If you want to use the card for system login, try <a href="https://aur.archlinux.org/packages/poldi-git/">Poldi</a>.</p><p>If you have questions or problems, please ask in the comments area below.</p><p>Wish you a nice journey playing with smartcards!</p><h3 id="reference">Reference</h3><p>A list of JavaCard goodies can be found at <a href="https://github.com/EnigmaBridge/javacard-curated-list">https://github.com/EnigmaBridge/javacard-curated-list</a></p>]]></content:encoded></item><item><title><![CDATA[Easy DDNS and SSL Certs for Your Domains Under Cloudflare]]></title><description><![CDATA[<p>Cloudflare is a popular DNS and CDN service provider. Being so, it lacks some basic functions like separate API keys for domains. So to mitigate the risk of putting your <strong>Global</strong> API Key everywhere on untrusted servers, I wrote a simple proxy for Cloudflare API with Rust and Rocket.rs.</p>]]></description><link>https://blog.amayume.net/easy-ddns-and-ssl-certs-for-your-domains-under-cloudflare/</link><guid isPermaLink="false">5c431c2ecf304c0f83c87f04</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Sat, 19 Jan 2019 12:53:53 GMT</pubDate><content:encoded><![CDATA[<p>Cloudflare is a popular DNS and CDN service provider. Being so, it lacks some basic functions like separate API keys for domains. So to mitigate the risk of putting your <strong>Global</strong> API Key everywhere on untrusted servers, I wrote a simple proxy for Cloudflare API with Rust and Rocket.rs.</p><p>Usage is simple: First clone the <a href="https://github.com/ProfFan/cloudflare-proxy-rs">repo</a>. Install Rust using <code>rustup</code> and compile. Install <code>diesel.rs</code> and setup PostgreSQL. Edit the <code>.env</code> file to provide DB information to the system and <code>diesel setup</code>. And now you are good to go!</p><p>Then create a user, a site and give the user privilege:</p><pre><code>cargo run --bin new_user

cargo run --bin new_site

cargo run --bin new_priv
</code></pre><p>And run the web app with:</p><pre><code>ROCKET_CFUSER=&lt;CF_EMAIL&gt; ROCKET_CFKEY=&lt;CF_KEY&gt; cargo run</code></pre><p>To call the API, simply use curl:</p><pre><code class="language-bash">curl --verbose  --header &quot;Content-Type: application/json&quot; \
  --request POST \
  --data '{&quot;user&quot;:&quot;&lt;USER&gt;&quot;,&quot;key&quot;:&quot;&lt;API_KEY&gt;&quot;,&quot;zone&quot;:&quot;amayume.net&quot;,&quot;rec&quot;:&quot;py.amayume.net&quot;,&quot;rectype&quot;:&quot;A&quot;, &quot;value&quot;:&quot;119.44.22.22&quot;}' \
  http://ddns.amayume.net:1337/update
</code></pre>
<p>Hope it can be useful for you :)</p>]]></content:encoded></item><item><title><![CDATA[Rethinking UE4 C++ Interop]]></title><description><![CDATA[<p>[This post is for my own notes]</p><h1 id="difference-in-normal-c-and-ue4-c-">Difference in Normal C++ and UE4 C++</h1><h2 id="cdo-v-s-constructor">CDO v.s. Constructor</h2><p>Basically the constructor of a C++ <code>UObject</code> is called in the following cases:</p><ul><li>Engine Start: construction of CDO, in <code>FEngineLoop::PreInit</code></li><li>BP Loading: also CDO, in <code>UClass::Serialize</code> (need confirm)</li><li>Object Loading(</li></ul>]]></description><link>https://blog.amayume.net/rethinking-the-design-of-pavilion/</link><guid isPermaLink="false">5bb72c57cf304c0f83c87ee7</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Sun, 07 Oct 2018 07:27:59 GMT</pubDate><content:encoded><![CDATA[<p>[This post is for my own notes]</p><h1 id="difference-in-normal-c-and-ue4-c-">Difference in Normal C++ and UE4 C++</h1><h2 id="cdo-v-s-constructor">CDO v.s. Constructor</h2><p>Basically the constructor of a C++ <code>UObject</code> is called in the following cases:</p><ul><li>Engine Start: construction of CDO, in <code>FEngineLoop::PreInit</code></li><li>BP Loading: also CDO, in <code>UClass::Serialize</code> (need confirm)</li><li>Object Loading(Spawn): normal constructor</li></ul><p>To determine the current mode, use:</p><pre><code class="language-c++">if (HasAnyFlags(RF_ClassDefaultObject)) {
    // Do CDO
} else {
    // Do normal init
}
</code></pre>
<p>So CDO is just for initializing of the <code>UPROPERTY</code> fields? Need to consider in design.</p><p>This is possibly the reason why the current version has so many bugs :)</p><p>BTW, for normal objects, just <code>new</code>ing it is sufficient. So CDO only applies to <code>UObject</code>s.</p><h2 id="gc">GC</h2><p>Big headache for me. UE4 by default garbage-collect everything that is too far away. For example, if you have a physics-enabled object that does not have collision with the ground, it will fall down infinitely, and then GC'ed as out-of-range. You may think this is not important, right? However think that when only the root component is set like that and it falls down too much-all other components behaves normally and all of a sudden your actor disappears entirely. Such situations are like nightmare for debugging-they are actually "normal" behaviors of the engine, thus leave no logs at all.</p><p>Another problem for GC is that it relies on references. And references rely on Unreal's Object System. If an object is not declared as <code>UObject</code>, it will not be considered in GC, which will result in memory leaks. Furthermore, if the non-<code>UObject</code> object refer to a GC'ed <code>UObject</code>, there will be a dangling pointer.</p><p>It is noteworthy that, Unreal Objects are also C++ objects. One may think, if we only use smart pointers in <code>UObject</code> for unmanaged objects and not refer to <code>UObject</code>s in unmanaged C++ code, the problem can be avoided in some cases.</p><p>However, consider the following situation: we have an <code>UObject</code> that needs to do some heavy lifting using a third-party library asynchronously. To do that, we call a function with a callback function as argument. Before the work is done, the <code>UObject</code> gets released by GC. Now the callback function becomes a dangling pointer. This example explains why it is impossible to avoid this problem in modern, async-heavy applications. </p><h1 id="interoperation-patterns">Interoperation Patterns</h1><p>An RPC-like solution is always nice to have. For example, the ongoing effort on integrating Cap'n Proto will enabled cross-thread and cross-process communication both in Unreal and between other components.</p><p>Cap'n Proto is nice in that it is message loop and Promise based. In this way exceptions can be handled without using exceptions. It also supports <code>-fno-rtti</code>. However the internals of Cap'n Proto is not well documented due to its relatively small user base. Still needs investigation.</p><p>ROS-based solutions can directly communicate with ROS without a bridge. However, they are usually deeply plagued by Boost, RTTI and other anti-patterns. The only standalone ROS library is cROS, which is limited in function and does not have a nice API.</p><p>[TODO: figure out the final architecture and start rebuilding Pavilion.]</p>]]></content:encoded></item><item><title><![CDATA[Random Thoughts on C++]]></title><description><![CDATA[<p>Time flies so fast that I suddenly realized that I have spent almost a whole year on my Unreal-based robot simulator project, <em>Pavilion</em>.</p><p>In the process of implementing that concept, I have grown up as a half-professional Unreal developer-yet the project is still a big mess with tons of spaghetti-like</p>]]></description><link>https://blog.amayume.net/some-thoughts-on-my-recent-efforts-in-robot-simulation/</link><guid isPermaLink="false">5bacb58f85c7214c910ef353</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Fri, 28 Sep 2018 03:43:18 GMT</pubDate><content:encoded><![CDATA[<p>Time flies so fast that I suddenly realized that I have spent almost a whole year on my Unreal-based robot simulator project, <em>Pavilion</em>.</p><p>In the process of implementing that concept, I have grown up as a half-professional Unreal developer-yet the project is still a big mess with tons of spaghetti-like code. This project finally made me realize that I am not omnipotent. I cannot single-handedly make a huge project work like magic in such a short time.</p><p>In retrospect, the current code base is non-maintainable to say the least. Functionalities are not clearly separated to modules, and modules have too much inter-dependencies that they have to be pulled together when you just need one, just like the famous-and-infamous <em>Boost</em>. Exceptions seems unavoidable. RTTI can only be eliminated by manual effort, or you will have to enable RTTI for the entire Unreal Engine.</p><h2 id="rtti">RTTI</h2><p>Who is to blame for this situation? RTTI is a controversial topic in C++ since its advent. It's sort of like GPL in the sense that it "infects" all the libraries that uses it, whether its shared (dynamically-linked) or static. The only way out is to expose all APIs in C, which then makes object-oriented programming a joke.</p><p>However, RTTI is not evil. It is indeed useful in many sense. It is just <strong>broken</strong> in implementation. However, many programmers actually <em>misuse</em> RTTI for cases where a virtual function would suffice. If that is not the case, <a href="https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html">LLVM-style RTTI</a> would in 99.9% of the cases, which does not require the C++ RTTI system to be on. Sadly, most C++ programmers simply takes the short path and enable RTTI, without considering the consequences.</p><h2 id="exceptions">Exceptions</h2><p>Exceptions are said to have "zero-cost" in modern C++. Yet we know that nothing in this world comes truly with <em>no cost.</em> Exceptions adds a lot of footprint to your binary. Misuse of exceptions creates alternate code paths that is implicit, that makes your codebase bug-prone. Throwing in constructors is also a highly <a href="https://stackoverflow.com/questions/810839/throwing-exceptions-from-constructors">controversial</a> issue.</p><p>At least 50% of the use of exceptions in C++ can be replaced without using it. For example, a throw in constructor when resource acquisition can be replaced for a non-throwing constructor and a throw in subsequent calls. Or even returning a <code>bool</code> in a <code>init()</code> method.</p><pre><code class="language-c++">
class A {
public:
    A() {
        bool connected = false;
        // connect to a server
        if (!connected) {
            throw NotConnectedException();
        }
    }
}

class B {
    bool connected = false;
public:
    B() {
        // connect to a server
    }
    
    void send() {
        if(!connected) {
            throw NotConnectedException();
        }
    }
}

class C {
    bool connected = false;
public:
    C() {
        
    }
    
    bool init() {
        // connect to a server
        return connected;
    }
}

</code></pre>
<p>As you can see in the above example, class <code>C</code> entirely avoided the use of exceptions, while abide by the RAII (Resource-Acquisition-Is-Initialization) principle. Again, the problem is that most programmers write code that <em>works</em>, not code that <em>works for everyone</em>.</p>]]></content:encoded></item><item><title><![CDATA[Notes on Programming in LLVM 6.0 and CUDA]]></title><description><![CDATA[<p>Disclaimer: This post assumes that you have adequate knowledge on basic usage of the LLVM library!</p><p>LLVM is great. Yes. But LLVM is full of caveats and especially when it comes to documentation. Most of the docs you can find on the Internet is for LLVM 3.x (3.5,</p>]]></description><link>https://blog.amayume.net/a-cheatsheet-to-programming-in-llvm-6-0/</link><guid isPermaLink="false">5ba3135585c7214c910ef34e</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Thu, 20 Sep 2018 04:33:54 GMT</pubDate><content:encoded><![CDATA[<p>Disclaimer: This post assumes that you have adequate knowledge on basic usage of the LLVM library!</p><p>LLVM is great. Yes. But LLVM is full of caveats and especially when it comes to documentation. Most of the docs you can find on the Internet is for LLVM 3.x (3.5, 3.9) and 4.x. LLVM is now 6.x and 7.x is on the way to release. Hopefully this post can help you solve some of your problems in learning LLVM and/or migration of LLVM versions.</p><h1 id="llvm-ir-and-cuda-related">LLVM IR and CUDA Related</h1><p>LLVM IR is an interesting language: it is used by lots of projects as an intermediate language, however it is in no way stable. The IR specification changes every release of LLVM and have no guaranteed compatibility.</p><p>This problem becomes even worse with the proprietary NVVM compiler, the <em>de facto</em> compiler for CUDA. NVVM is still using the LLVM 3.4 version as its base (as of CUDA 9.2), as you can see here:</p><pre><code>//
// Generated by NVIDIA NVVM Compiler
//
// Compiler Build ID: CL-23757830
// Cuda compilation tools, release 9.2, V9.2.64
// Based on LLVM 3.4svn
//</code></pre><p>Thus it will not be able to read in the IR generated by LLVM with any version larger than 4.0. What made it worse is that there is actually no tool to convert between IR versions. This bites me when I was porting the CUDA functionality of Stanford's Terra language to LLVM 6.0. Every single function call succeeded without a warning, however the program does not work at all! And when I checked the output of NVVM, it is not empty. No. It is <strong>comment-only</strong>! Kind of like I was completely fooled by the non-empty string size.</p><p>To solve this incompatibility, we have to resort to the open-source counterpart of NVVM, the NVPTX backend of LLVM. It is not tuned by folks at NVIDIA, hence possibly lower performance. It is relatively easy to use, with one page of <a href="https://llvm.org/docs/NVPTXUsage.html">documentation</a> that has plenty of information. Just you need to link the <code>libdevice</code> yourself, or you will get errors when doing anything with a math function.</p><h1 id="llvm-api-related">LLVM API Related</h1><p>The LLVM API, just like the IR, is also not stable between releases.</p><h2 id="linker-api">Linker API</h2><p>Almost all the information you can find on the Internet is like <a href="https://stackoverflow.com/questions/6435400/using-llvmlinker-to-programatically-find-unresolved-externals">this</a>:</p><pre><code class="language-c++">llvm::Linker linker(&quot;clang_test&quot;, &quot;clang_test&quot;, lc, llvm::Linker::Verbose);

std::string error;
if( linker.LinkInModule( new_module, &amp;error ) || !error.empty() ) {
    printf( &quot;link error\n&quot; );
    return -3;
}

llvm::Module* composite_module = linker.getModule();
if( composite_module == NULL ) {
    printf( &quot;link error\n&quot; );
    return -3;
}
</code></pre>
<p>Which is not correct for LLVM &gt;3.4. In fact, LLVM now standardized all APIs to be <strong>Module-centric</strong>, i.e., everything is modular and these strings are gone. This means a little bit more boilerplate code but a lot more grasp for the user on understanding what is going on under the hood.</p><p>See it yourself:</p><pre><code class="language-c++">auto libdevice = &quot;/usr/local/lib/libdevice.bc&quot;; // The library you want to link in
llvm::SmallString&lt;2048&gt; ErrMsg;
auto MB = llvm::MemoryBuffer::getFile(libdevice); // Get the contents of the lib file
auto E_LDEVICE = llvm::parseBitcodeFile(MB-&gt;get()-&gt;getMemBufferRef(), M-&gt;getContext()); // Detroit: Become Module

if (auto Err = E_LDEVICE.takeError()) { // New error handling
    llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), &quot;[CUDA Error] &quot;);
    return;
}

auto &amp;LDEVICE = *E_LDEVICE;

auto TargetMachine = Target-&gt;createTargetMachine(&quot;nvptx64-nvidia-cuda&quot;, cpuopt, Features, opt, RM);

LDEVICE-&gt;setTargetTriple(&quot;nvptx64-nvidia-cuda&quot;);
LDEVICE-&gt;setDataLayout(TargetMachine-&gt;createDataLayout());

llvm::Linker Linker(*M); // M is the reference of the module you want to link libdevice in
Linker.linkInModule(std::move(LDEVICE));
</code></pre>
<p>As you can see above, now you need to first read in and parse the module to get a <code>unique_ptr</code> to the <code>llvm::Module</code> you want to link in, and <em>create</em> a <code>llvm::Linker</code> with the target module <code>M</code>, then link the library in by calling <code>linker.linkInModule</code>. Also note that how LLVM is now using smart pointers to handle memory: previously there are APIs like  <code>linker.releaseModule</code>, and now they are completely history.</p><p>To be continued :)</p>]]></content:encoded></item><item><title><![CDATA[Bringing Criticism to Learning Deep Learning]]></title><description><![CDATA[<p>Disclaimer: This article is written while the author is drunk. Expect errors and grammar mistakes.</p><p>There's been something lurking in my mind for years–something that can be described as a dream, or more precisely, as an objective. It is resurrected every time I recalled the biology seminars I took.</p>]]></description><link>https://blog.amayume.net/bringing-scientific-methods-to-deep-learning/</link><guid isPermaLink="false">5b58622f07f6482614d170d0</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Wed, 25 Jul 2018 12:16:59 GMT</pubDate><content:encoded><![CDATA[<p>Disclaimer: This article is written while the author is drunk. Expect errors and grammar mistakes.</p><p>There's been something lurking in my mind for years–something that can be described as a dream, or more precisely, as an objective. It is resurrected every time I recalled the biology seminars I took. I can remember all the fierce firefight with professors and fellow students over my slides and presentations. Every seminar session is a battlefield with people desperately searching for logical errors, data inconsistencies, misleading figures, and even grammar mistakes, to falsify the presenter's work.</p><p>After I entered the field of computer science and engineering, things become much different. In nearly every single seminar I attended, I can only see peace and friendly discussion. There's been no one asking tough questions. In a biologist's eyes, tons of deep learning papers lacks enough evidence to prove their conclusion. Yet they got published into top conferences and journals. It seems that people nowadays only cares about performance.</p><p>Learning deep learning <em>concretely</em> is merely a distant dream.</p>]]></content:encoded></item><item><title><![CDATA[Repairing Windows DriverStore is not impossible!]]></title><description><![CDATA[<p>Windows 10 disappointed me again today–my Realtek audio drivers suddenly stopped to work simply after a reboot. All I have now is mysterious "Driver is not signed" error. I tried to remove the unsigned driver (which is <code>portcls.sys</code> by the way) and run <code>sfc /scannow</code> to repair the</p>]]></description><link>https://blog.amayume.net/repairing-windows-driverstore-is-not-impossible/</link><guid isPermaLink="false">5b4db8c407f6482614d170c6</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Tue, 17 Jul 2018 10:45:31 GMT</pubDate><content:encoded><![CDATA[<p>Windows 10 disappointed me again today–my Realtek audio drivers suddenly stopped to work simply after a reboot. All I have now is mysterious "Driver is not signed" error. I tried to remove the unsigned driver (which is <code>portcls.sys</code> by the way) and run <code>sfc /scannow</code> to repair the corrupted file. Did not work. A <code>dism</code> repair worked, but that did not help to solve the sfc issues.</p><p>I then made a stupid mistake–I deleted the drivers entirely, a.k.a. all relevant files in <code>C:\WINDOWS\INF</code>, <code>C:\WINDOWS\System32\drivers</code> and most importantly, <code>C:\WINDOWS\System32\drivers\DriverStore</code>. After that I run <code>sfc</code> again and it still does not work!</p><p>After cursing Microsoft for making such a silly system repair utility, I return to try to manually diagnose the issue. I first manually copied back all files to <code>INF</code> and <code>drivers</code>, but the driver installations still did not go through. I then try putting back the <code>DriverStore</code> folders–then I found out that I completely forgot their proper names!</p><p>Normal Windows Event Viewer only shows a <code>0x2</code> error code, so I googled tons of pages to find the location of a proper setup log. It was two hours later that I found the location: <code>C:\Windows\Inf\setupapi.dev.log</code></p><p>After that things went smoothly. You just need to search for <strong>Error 2</strong> in the log, and the proper path revealed itself.</p><pre><code>!!!  flq:                     Source Media: SPFQOPERATION_ABORT (C:\Windows\System32\DriverStore\FileRepository\wdmaudio.inf_amd64_179dade7dec66387\drmk.sys).
!!!  flq:                     Error 2: The system cannot find the file specified.
!!!  flq:                     FileQueueCommit aborting!
!!!  flq:                     Error 2: The system cannot find the file specified.</code></pre><p>Now you just need to copy the directory in <code>WinSxS</code> to the <code>DriverStore</code> and rename. After that the audio drivers are finally back working.</p>]]></content:encoded></item><item><title><![CDATA[[CoreRC] rcgenmsg: ROS Message Transpiler]]></title><description><![CDATA[<p>There is actually not much to say but I still want to keep my blogging streak :) So this is about the ROS message transpiler I developed with Rust. It uses the <a href="https://github.com/pest-parser/pest">Pest</a> parser and <a href="https://github.com/sunng87/handlebars-rust">handlebars-rust</a> to convert ROS msg to Cap'n Proto format. This is my first time using Rust</p>]]></description><link>https://blog.amayume.net/corerc-rcgenmsg-ros-message-transpiler/</link><guid isPermaLink="false">5b3c6eeb07f6482614d170c1</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Wed, 04 Jul 2018 07:49:54 GMT</pubDate><content:encoded><![CDATA[<p>There is actually not much to say but I still want to keep my blogging streak :) So this is about the ROS message transpiler I developed with Rust. It uses the <a href="https://github.com/pest-parser/pest">Pest</a> parser and <a href="https://github.com/sunng87/handlebars-rust">handlebars-rust</a> to convert ROS msg to Cap'n Proto format. This is my first time using Rust on a serious project so expect bad code quality :(</p><h1 id="parsing">Parsing</h1><p>Parsing is mostly documented in the previous <a href="https://blog.amayume.net/using-pest-to-convert-ros-message-definitions-to-capn-proto-format/">post</a>. However the resulting data structure is Pest's own <code>Iter</code>-based format. We need to convert it into an intermediate representation with the <code>Serialize</code> trait required by <code>handlebars-rust</code>. This is done via a custom <code>ASTDef</code> type with the required traits. Though I think there may be better methods to do that, this is the best I can come up with.</p>
<pre><code class="language-rust">#[derive(Serialize, Deserialize, Debug)]
pub struct ConstDef {
    typ: String,
    name: String,
    val: String,
    comment: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct FieldDef {
    typ: String,
    name: String,
    comment: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ASTDef {
    consts: Vec&lt;ConstDef&gt;,
    fields: Vec&lt;FieldDef&gt;,
}

fn pest_to_ast(pes: &amp;pest::iterators::Pair&lt;Rule&gt;) -&gt; Option&lt;ASTDef&gt; {
    let mut ast = ASTDef {
        consts: Vec::new(),
        fields: Vec::new(),
    };

    // Because ident_list is silent, the iterator will contain idents
    for pair in pes.clone().into_inner() {
        let span = pair.clone().into_span();
        match pair.as_rule() {
            Rule::constant =&gt; {
                let mut typ: Option&lt;String&gt; = None;
                let mut name: Option&lt;String&gt; = None;
                let mut val: Option&lt;String&gt; = None;
                let mut comment: Option&lt;String&gt; = None;

                for inner_pair in pair.clone().into_inner() {
                    let inner_span = inner_pair.clone().into_span();
                    match inner_pair.as_rule() {
                        Rule::itype =&gt; {
                            typ = Some(inner_pair.as_str().to_string());
                        }
                        Rule::identifier =&gt; {
                            name = Some(inner_pair.as_str().to_string());
                        }
                        Rule::value =&gt; {
                            val = Some(inner_pair.as_str().to_string());
                        }
                        Rule::comment =&gt; {
                            comment = Some(inner_pair.as_str().to_string());
                        }
                        _ =&gt; panic!(&quot;ERR {:?}:   {}&quot;, inner_pair.as_rule(), inner_span.as_str()),
                    };
                }

                ast.consts.push(ConstDef {
                    typ: typ.expect(&quot;No type detected&quot;),
                    name: name.expect(&quot;No name detected&quot;),
                    val: val.expect(&quot;No val detected&quot;),
                    comment: comment.unwrap_or(&quot;&quot;.to_string()),
                })
            }
            Rule::variable =&gt; {
                ...
            }
            Rule::comment =&gt; {
                // println!(&quot;{}&quot;, pair.as_str());
            }
            _ =&gt; panic!(&quot;ERR {:?}:   {}&quot;, pair.as_rule(), span.as_str()),
        }
    }

    return Some(ast);
}
</code></pre>
<p>As you can see, every node needs to be filled manually. If you have a better method, please comment below :)</p>
<h1 id="code-generation">Code Generation</h1><p>The Cap'n Proto code is then generated by <code>handlebars-rust</code>. Because we implemented the <code>Serialize</code> trait in our AST, we can now simply use <code>&amp;serde_json::to_value(&amp;ast)</code> to feed JSON data to <code>handlebars-rust</code>.</p><pre><code class="language-rust">fn compile_file(ast: ASTDef) {
    use handlebars::Handlebars;

    let reg = Handlebars::new();

    let template =
r###&quot;struct BatteryState {
{{#each consts as |c| ~}}
  const {{c.name}} @0 : {{c.typ}} = {{ c.val }};{{ c.comment }}
{{/each ~}}

{{#each fields as |f| ~}}
  {{f.name}} @0 : {{f.typ}};{{ f.comment }}
{{/each ~}}
}&quot;###;
    // render without register
    println!(&quot;{}&quot;, reg.render_template( template, &amp;serde_json::to_value(&amp;ast).unwrap()).unwrap());
}
</code></pre>
<h1 id="conclusion">Conclusion</h1><p>This project is fairly simple and really nice for a Rust newcomer like myself. Next step would probably be writing the <code>roscpp</code> compatibility layer in Rust and export with C++. Anyway, stay tuned :)</p>]]></content:encoded></item><item><title><![CDATA[Using pest to Parse ROS Message Definitions]]></title><description><![CDATA[<p>This is a continuation of the <a href="https://blog.amayume.net/using-peg-js-to-convert-ros-message-definitions-to-capn-proto-format/">previous post</a> using PEG.js to parse ROS message definitions. However, it is alway better if we can use Rust :)</p><h1 id="code">Code</h1><p>The code is hosted at <a href="https://github.com/CoreRC/rcgenmsg">https://github.com/CoreRC/rcgenmsg</a>. For now it's only a parser adapted from the official <a href="https://github.com/pest-parser/pest">pest</a> documentation. The</p>]]></description><link>https://blog.amayume.net/using-pest-to-convert-ros-message-definitions-to-capn-proto-format/</link><guid isPermaLink="false">5b3b385707f6482614d170bf</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Tue, 03 Jul 2018 08:57:00 GMT</pubDate><content:encoded><![CDATA[<p>This is a continuation of the <a href="https://blog.amayume.net/using-peg-js-to-convert-ros-message-definitions-to-capn-proto-format/">previous post</a> using PEG.js to parse ROS message definitions. However, it is alway better if we can use Rust :)</p><h1 id="code">Code</h1><p>The code is hosted at <a href="https://github.com/CoreRC/rcgenmsg">https://github.com/CoreRC/rcgenmsg</a>. For now it's only a parser adapted from the official <a href="https://github.com/pest-parser/pest">pest</a> documentation. The parser definition is listed below:</p><pre><code class="language-c">// ROS Message Definition Parser
// ==========================

file = {
    (result)*
}
result = _{
    (&quot; &quot; | &quot;\t&quot;)* ~ (comment | definition)? ~ sp ~ comment? ~ linebreak+
}

sp = _{
    (&quot; &quot; | &quot;\t&quot;)*
}

linebreak = _{
    &quot;\n&quot; | &quot;\r&quot;
}

types = {
    (&quot;bool&quot; | &quot;uint8&quot; | &quot;float32&quot; | &quot;string&quot; | &quot;Header&quot; | !(&quot;\n&quot; | &quot;\r&quot; | &quot;\t&quot;)+)
}

array = {
    types ~ (&quot;[]&quot;)
}

itype = {
    array | types
}

identifier = {
    ('a'..'z' | 'A'..'Z' | &quot;_&quot;)+
}

value = {
    ('a'..'z' | 'A'..'Z' | '0'..'9')+
}

constant = {
    itype ~ sp ~ identifier ~ sp ~ &quot;=&quot; ~ sp ~ value
}

variable = {
    itype ~ sp ~ identifier ~ sp ~ !(&quot;=&quot;)
}

definition = {
    (variable | constant)
}

nonl = _{ !linebreak ~ any }
comment = { &quot;#&quot; ~ nonl* }
</code></pre>
<h1 id="test">Test</h1><p>We tested it with the following ROS msg definition file with constants, variables, arrays and inline comments:</p><pre><code class="language-bash">
# Constants are chosen to match the enums in the linux kernel
# defined in include/linux/power_supply.h as of version 3.7
# The one difference is for style reasons the constants are
# all uppercase not mixed case.

# Power supply status constants
uint8 POWER_SUPPLY_STATUS_UNKNOWN = 0
uint8 POWER_SUPPLY_STATUS_CHARGING = 1
uint8 POWER_SUPPLY_STATUS_DISCHARGING = 2
uint8 POWER_SUPPLY_STATUS_NOT_CHARGING = 3
uint8 POWER_SUPPLY_STATUS_FULL = 4

# Power supply health constants
uint8 POWER_SUPPLY_HEALTH_UNKNOWN = 0
uint8 POWER_SUPPLY_HEALTH_GOOD = 1
uint8 POWER_SUPPLY_HEALTH_OVERHEAT = 2 # test
uint8 POWER_SUPPLY_HEALTH_DEAD = 3
uint8 POWER_SUPPLY_HEALTH_OVERVOLTAGE = 4
uint8 POWER_SUPPLY_HEALTH_UNSPEC_FAILURE = 5
uint8 POWER_SUPPLY_HEALTH_COLD = 6
uint8 POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE = 7
uint8 POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE = 8

# Power supply technology (chemistry) constants
uint8 POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0
uint8 POWER_SUPPLY_TECHNOLOGY_NIMH = 1
uint8 POWER_SUPPLY_TECHNOLOGY_LION = 2
uint8 POWER_SUPPLY_TECHNOLOGY_LIPO = 3
uint8 POWER_SUPPLY_TECHNOLOGY_LIFE = 4
uint8 POWER_SUPPLY_TECHNOLOGY_NICD = 5
uint8 POWER_SUPPLY_TECHNOLOGY_LIMN = 6

Header  header
float32 voltage          # Voltage in Volts (Mandatory)
float32 current          # Negative when discharging (A)  (If unmeasured NaN)
float32 charge           # Current charge in Ah  (If unmeasured NaN)
float32 capacity         # Capacity in Ah (last full capacity)  (If unmeasured NaN)
float32 design_capacity  # Capacity in Ah (design capacity)  (If unmeasured NaN)
float32 percentage       # Charge percentage on 0 to 1 range  (If unmeasured NaN)
uint8   power_supply_status     # The charging status as reported. Values defined above
uint8   power_supply_health     # The battery health metric. Values defined above
uint8   power_supply_technology # The battery chemistry. Values defined above
bool    present          # True if the battery is present

float32[] cell_voltage   # An array of individual cell voltages for each cell in the pack
                         # If individual voltages unknown but number of cells known set each to NaN
string location          # The location into which the battery is inserted. (slot number or plug)
string serial_number     # The best approximation of the battery serial number
</code></pre>
<p>And the result is here:</p>
<pre><code>Processing: hello.txt
LINE:    # Constants are chosen to match the enums in the linux kernel
LINE:    # defined in include/linux/power_supply.h as of version 3.7
LINE:    # The one difference is for style reasons the constants are
LINE:    # all uppercase not mixed case.
LINE:    # Power supply status constants
LINE:    uint8 POWER_SUPPLY_STATUS_UNKNOWN = 0
PART itype:   uint8
PART identifier:   POWER_SUPPLY_STATUS_UNKNOWN
PART value:   0
LINE:    uint8 POWER_SUPPLY_STATUS_CHARGING = 1
PART itype:   uint8
PART identifier:   POWER_SUPPLY_STATUS_CHARGING
PART value:   1
LINE:    uint8 POWER_SUPPLY_STATUS_DISCHARGING = 2
PART itype:   uint8
PART identifier:   POWER_SUPPLY_STATUS_DISCHARGING
PART value:   2
LINE:    uint8 POWER_SUPPLY_STATUS_NOT_CHARGING = 3
PART itype:   uint8
PART identifier:   POWER_SUPPLY_STATUS_NOT_CHARGING
PART value:   3
LINE:    uint8 POWER_SUPPLY_STATUS_FULL = 4
PART itype:   uint8
PART identifier:   POWER_SUPPLY_STATUS_FULL
PART value:   4
LINE:    # Power supply health constants
LINE:    uint8 POWER_SUPPLY_HEALTH_UNKNOWN = 0
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_UNKNOWN
PART value:   0
LINE:    uint8 POWER_SUPPLY_HEALTH_GOOD = 1
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_GOOD
PART value:   1
LINE:    uint8 POWER_SUPPLY_HEALTH_OVERHEAT = 2
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_OVERHEAT
PART value:   2
LINE:    # test
LINE:    uint8 POWER_SUPPLY_HEALTH_DEAD = 3
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_DEAD
PART value:   3
LINE:    uint8 POWER_SUPPLY_HEALTH_OVERVOLTAGE = 4
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_OVERVOLTAGE
PART value:   4
LINE:    uint8 POWER_SUPPLY_HEALTH_UNSPEC_FAILURE = 5
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
PART value:   5
LINE:    uint8 POWER_SUPPLY_HEALTH_COLD = 6
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_COLD
PART value:   6
LINE:    uint8 POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE = 7
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE
PART value:   7
LINE:    uint8 POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE = 8
PART itype:   uint8
PART identifier:   POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
PART value:   8
LINE:    # Power supply technology (chemistry) constants
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_UNKNOWN
PART value:   0
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_NIMH = 1
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_NIMH
PART value:   1
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_LION = 2
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_LION
PART value:   2
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_LIPO = 3
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_LIPO
PART value:   3
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_LIFE = 4
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_LIFE
PART value:   4
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_NICD = 5
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_NICD
PART value:   5
LINE:    uint8 POWER_SUPPLY_TECHNOLOGY_LIMN = 6
PART itype:   uint8
PART identifier:   POWER_SUPPLY_TECHNOLOGY_LIMN
PART value:   6
LINE:    Header  header
PART itype:   Header
PART identifier:   header
LINE:    float32 voltage          # Voltage in Volts (Mandatory)
PART itype:   float32
PART identifier:   voltage
PART comment:   # Voltage in Volts (Mandatory)
LINE:    float32 current          # Negative when discharging (A)  (If unmeasured NaN)
PART itype:   float32
PART identifier:   current
PART comment:   # Negative when discharging (A)  (If unmeasured NaN)
LINE:    float32 charge           # Current charge in Ah  (If unmeasured NaN)
PART itype:   float32
PART identifier:   charge
PART comment:   # Current charge in Ah  (If unmeasured NaN)
LINE:    float32 capacity         # Capacity in Ah (last full capacity)  (If unmeasured NaN)
PART itype:   float32
PART identifier:   capacity
PART comment:   # Capacity in Ah (last full capacity)  (If unmeasured NaN)
LINE:    float32 design_capacity  # Capacity in Ah (design capacity)  (If unmeasured NaN)
PART itype:   float32
PART identifier:   design_capacity
PART comment:   # Capacity in Ah (design capacity)  (If unmeasured NaN)
LINE:    float32 percentage       # Charge percentage on 0 to 1 range  (If unmeasured NaN)
PART itype:   float32
PART identifier:   percentage
PART comment:   # Charge percentage on 0 to 1 range  (If unmeasured NaN)
LINE:    uint8   power_supply_status     # The charging status as reported. Values defined above
PART itype:   uint8
PART identifier:   power_supply_status
PART comment:   # The charging status as reported. Values defined above
LINE:    uint8   power_supply_health     # The battery health metric. Values defined above
PART itype:   uint8
PART identifier:   power_supply_health
PART comment:   # The battery health metric. Values defined above
LINE:    uint8   power_supply_technology # The battery chemistry. Values defined above
PART itype:   uint8
PART identifier:   power_supply_technology
PART comment:   # The battery chemistry. Values defined above
LINE:    bool    present          # True if the battery is present
PART itype:   bool
PART identifier:   present
PART comment:   # True if the battery is present
LINE:    float32[] cell_voltage   # An array of individual cell voltages for each cell in the pack
PART itype:   float32[]
PART identifier:   cell_voltage
PART comment:   # An array of individual cell voltages for each cell in the pack
LINE:    # If individual voltages unknown but number of cells known set each to NaN
LINE:    string location          # The location into which the battery is inserted. (slot number or plug)
PART itype:   string
PART identifier:   location
PART comment:   # The location into which the battery is inserted. (slot number or plug)
LINE:    string serial_number     # The best approximation of the battery serial number
PART itype:   string
PART identifier:   serial_number
PART comment:   # The best approximation of the battery serial number
</code></pre>
<p>We can see it correctly parsed both normal types and arrays correctly.</p>
]]></content:encoded></item><item><title><![CDATA[Made a new logo for the CoreRC Project]]></title><description><![CDATA[<p>So I made a new logo for the robotics middleware project :)</p><p>Please stay tuned at <a href="https://github.com/CoreRC">https://github.com/CoreRC</a> :)</p><figure class="kg-image-card"><img src="https://blog.amayume.net/content/images/2018/07/OpenRC.png" class="kg-image"><figcaption>CoreRC Logo</figcaption></figure>]]></description><link>https://blog.amayume.net/made-a-new-logo-for-the-corerc-project/</link><guid isPermaLink="false">5b39bfa607f6482614d170bc</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Mon, 02 Jul 2018 06:04:31 GMT</pubDate><content:encoded><![CDATA[<p>So I made a new logo for the robotics middleware project :)</p><p>Please stay tuned at <a href="https://github.com/CoreRC">https://github.com/CoreRC</a> :)</p><figure class="kg-image-card"><img src="https://blog.amayume.net/content/images/2018/07/OpenRC.png" class="kg-image"><figcaption>CoreRC Logo</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Booklist: For Computer Science Students]]></title><description><![CDATA[<p>A book list for prospective CS students.</p><ol><li>《哥德尔、艾舍尔、巴赫:集异璧之大成》（大名鼎鼎的GEB/MUST READ no matter what）</li><li>John Stillwell 《<a href="https://www.amazon.cn/gp/product/B004TJ4XNS/">数学及其历史</a>》(Math step by step)</li><li>《<a href="https://www.amazon.cn/gp/product/B003YFKSSW/ref=oh_aui_detailpage_o09_s00?ie=UTF8&amp;psc=1">数学桥:对高等数学的一次观赏之旅</a>》(Especially Recommended for Newbies)</li><li>Felix Klein《<a href="https://www.amazon.cn/gp/product/B001HKX6ZS/ref=oh_aui_detailpage_o09_s00?ie=UTF8&amp;psc=1">高观点下的初等数学(全3册)</a>》(Show you the real magic of math)</li><li>Kailai Chung 《<a href="https://www.amazon.cn/gp/product/B003GXFBMA/ref=oh_aui_detailpage_o01_s00?ie=UTF8&amp;psc=1">初等概率论(第4版)(英文版)</a>》(Very good introductory material)</li></ol>]]></description><link>https://blog.amayume.net/booklist-for-computer-science-students/</link><guid isPermaLink="false">5b3628bc07f6482614d170b8</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Fri, 29 Jun 2018 13:03:42 GMT</pubDate><content:encoded><![CDATA[<p>A book list for prospective CS students.</p><ol><li>《哥德尔、艾舍尔、巴赫:集异璧之大成》（大名鼎鼎的GEB/MUST READ no matter what）</li><li>John Stillwell 《<a href="https://www.amazon.cn/gp/product/B004TJ4XNS/">数学及其历史</a>》(Math step by step)</li><li>《<a href="https://www.amazon.cn/gp/product/B003YFKSSW/ref=oh_aui_detailpage_o09_s00?ie=UTF8&amp;psc=1">数学桥:对高等数学的一次观赏之旅</a>》(Especially Recommended for Newbies)</li><li>Felix Klein《<a href="https://www.amazon.cn/gp/product/B001HKX6ZS/ref=oh_aui_detailpage_o09_s00?ie=UTF8&amp;psc=1">高观点下的初等数学(全3册)</a>》(Show you the real magic of math)</li><li>Kailai Chung 《<a href="https://www.amazon.cn/gp/product/B003GXFBMA/ref=oh_aui_detailpage_o01_s00?ie=UTF8&amp;psc=1">初等概率论(第4版)(英文版)</a>》(Very good introductory material)</li><li>Richard Feynman《<a href="https://www.amazon.cn/gp/product/B00116PZEI/ref=oh_aui_detailpage_o06_s00?ie=UTF8&amp;psc=1">费恩曼物理学讲义(第1卷)</a>》(You know why)</li><li>R. Bryant, et al. 《<a href="https://www.amazon.cn/gp/product/B004N85EKG/ref=oh_aui_detailpage_o05_s00?ie=UTF8&amp;psc=1">深入理解计算机系统(英文版•第2版)</a>》(MUST READ for CS)</li><li>R. Graham《<a href="https://www.amazon.cn/gp/product/B0011AC8FS/ref=oh_aui_detailpage_o08_s00?ie=UTF8&amp;psc=1">具体数学:计算机科学基础(英文版第2版)</a>》(Take this as an appetizer for TAOCP)</li><li>P. Horowitz, W. Hill《<a href="https://www.amazon.cn/dp/0521809266/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1530276950&amp;sr=1-1&amp;keywords=the+art+of+electronics">The Art of Electronics</a>》（中文《电子学》 MUST READ for all prospective engineers）</li><li>Use bookfi.net if you want free PDFs :)</li></ol>]]></content:encoded></item><item><title><![CDATA[Using PEG.js to Convert ROS Message Definitions to Cap'n Proto Format]]></title><description><![CDATA[<p>[This is an ongoing project, expect updates.]</p><p>In my make-my-own-ROS project, I want to ensure compatibility with the original ROS ecosystem, so I need to provide the same set of functionality like <code>roscpp</code>. This is can be achieved in a number of steps, of which the first is to convert</p>]]></description><link>https://blog.amayume.net/using-peg-js-to-convert-ros-message-definitions-to-capn-proto-format/</link><guid isPermaLink="false">5b34e9fc07f6482614d170b6</guid><dc:creator><![CDATA[Fan Jiang]]></dc:creator><pubDate>Thu, 28 Jun 2018 14:10:38 GMT</pubDate><content:encoded><![CDATA[<p>[This is an ongoing project, expect updates.]</p><p>In my make-my-own-ROS project, I want to ensure compatibility with the original ROS ecosystem, so I need to provide the same set of functionality like <code>roscpp</code>. This is can be achieved in a number of steps, of which the first is to convert the message definitions. Initially I want to use Rust for that, but upon discovering an online PEG debugger, I decided to use that first.</p>
<h2 id="procedure">Procedure</h2><p>Really simple. Just write grammar-&gt;test-&gt;change PEG-&gt;test-&gt;...</p><p>The final version of the PEG definition is posted below:</p><pre><code class="language-c">// ROS Message Definition Parser
// ==========================

file = (result)*
result = sp (comment / definition)* linebreak

_ &quot;whitespace&quot;
  = [ \t\n\r]*

sp &quot;space&quot;
  = $[ \t]*

linebreak = [\n\r]

types &quot;types&quot; = (&quot;bool&quot; / &quot;uint8&quot; / &quot;float32&quot; / &quot;string&quot; / &quot;Header&quot;)

array &quot;array&quot; = types (&quot;[]&quot;)

type &quot;type&quot; = array / types

identifier &quot;identifier&quot; = $[a-zA-Z_]+

constant &quot;constant&quot; = type sp identifier sp [=] sp [0-9]+

variable &quot;variable&quot; = (type / array) sp identifier sp ![=]
definition &quot;definition&quot; = (variable / constant)

string &quot;string&quot; = sp $[^\n\r]+

comment &quot;comment&quot; = (['#']) string
</code></pre>
<h2 id="result">Result</h2><p>Thanks to the amazing <a href="http://phrogz.net/js/pegsh/">web-based PEG.js debugger</a> by phrogz, I was able to visualize the result easily in my browser :) As you can see, all tokens are differentiated with different colors. The next step would be translating from grammar tree to Cap'n Proto code!</p><figure class="kg-image-card"><img src="https://blog.amayume.net/content/images/2018/06/image-8.png" class="kg-image"><figcaption>Final Result</figcaption></figure>]]></content:encoded></item></channel></rss>