This service tests the validity of an RSS 2.0 feed, checking to see that it follows the rules of the RSS specification. For advice from the RSS Advisory Board on how to implement RSS and handle issues such as enclosures and HTML encoding, read the RSS Best Practices Profile. This checker is also a validator of Atom and RSS 1.0 feeds.

Use this tester regularly to ensure that your RSS feed continues to work well in the wide audience of RSS readers, podcast clients and other software that supports the format.

 

Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

  • line 262, column 1: Missing atom:link with rel="self" [help]

    	</channel>
     ^

Source: http://doclic.eu/blog/rss-en.xml

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
  2. <rss version="2.0">
  3. <channel>
  4. <title>doclic's blog!</title>
  5. <link>http://doclic.eu/blog/index-en.htm</link>
  6. <description>ramblings about computers</description>
  7. <language>en</language>
  8. <managingEditor>doclic@tutanota.com (doclic)</managingEditor>
  9. <webMaster>doclic@tutanota.com (doclic)</webMaster>
  10. <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
  11. <generator>doclic's garbage code</generator>
  12. <docs>https://www.rssboard.org/rss-2-0</docs>
  13. <ttl>1440</ttl>
  14. <image>
  15. <url>http://doclic.eu/badges/ME.gif</url>
  16. <title>doclic's blog!</title>
  17. <link>http://doclic.eu/blog/index-en.htm</link>
  18. <width>88</width>
  19. <height>31</height>
  20. </image>
  21. <skipHours>
  22. <hour>1</hour>
  23. <hour>2</hour>
  24. <hour>3</hour>
  25. <hour>4</hour>
  26. <hour>5</hour>
  27. <hour>6</hour>
  28. <hour>7</hour>
  29. <hour>8</hour>
  30. </skipHours>
  31. <item>
  32. <title>Use .ZIP files for assets</title>
  33. <link>http://doclic.eu/blog/use-zip-files-for-assets.htm</link>
  34. <author>doclic@tutanota.com (doclic)</author>
  35. <guid isPermaLink="true">http://doclic.eu/blog/use-zip-files-for-assets.htm</guid>
  36. <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
  37. <source url="http://doclic.eu/rss-en.xml">
  38. doclic's blog!
  39. </source>
  40. <description>
  41. &lt;p&gt;
  42. I like to make small games for home computers and sharing them around. Every
  43. game loads assets such as images or sound effects. These assets are often stored
  44. in a directory next to the game's executable file, which isn't ideal because
  45. this method requires the programmer to share their game in an archive format
  46. such as a
  47. &lt;a href="https://en.wikipedia.org/wiki/Tar_%28computing%29?useskin=monobook"&gt;&lt;code&gt;.TAR&lt;/code&gt;&lt;/a&gt;
  48. or
  49. &lt;a href="https://en.wikipedia.org/wiki/ZIP_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.ZIP&lt;/code&gt;&lt;/a&gt;
  50. file that contains everything the game needs. The game can no longer run on its
  51. own.
  52. &lt;/p&gt;
  53. &lt;p&gt;
  54. Because of this, I looked for better ways to store game assets and found Sherief
  55. Farouk's blog post
  56. &lt;a href="https://sherief.fyi/post/atomic-application/"&gt;"The Atomic Application"&lt;/a&gt;
  57. where the author talks about the
  58. &lt;a href="https://icculus.org/physfs/"&gt;PhysicsFS&lt;/a&gt;
  59. library. This library can read an archive stored in the game's source code and
  60. load assets from there.
  61. &lt;br&gt;
  62. It's a well-made tool however it's way too complex for
  63. what I want to do. It supports 13 archive formats including bespoke ones like
  64. the &lt;code&gt;.ISO&lt;/code&gt; format used on CD-ROMs. Storing the archive in the source code also
  65. means that the whole archive is stored in RAM for the full runtime of our game.
  66. Not ideal.
  67. &lt;/p&gt;
  68. &lt;p&gt;
  69. After a bit more searching, I found this blog post by Dominic Szablewski:
  70. &lt;a href="https://phoboslab.org/log/2024/09/qop"&gt;"A Simple Archive Format for Self-Contained Executables"&lt;/a&gt;.
  71. It starts with a hack where the assets are embedded in the game's program by
  72. concatenating the program with an asset archive using a command such as
  73. &lt;a href="https://manpages.debian.org/trixie/coreutils/cat.1"&gt;&lt;code&gt;cat(1)&lt;/code&gt;&lt;/a&gt;.
  74. This means that the assets aren't loaded by the OS when the program is ran,
  75. however we now have to do that manually with our code. This requires knowing the
  76. path to our program binary which is inherently platform-specific. The author of
  77. the post gives us
  78. &lt;a href="https://github.com/phoboslab/qop/blob/master/example.c#L17-L35"&gt;a function&lt;/a&gt;
  79. that works nicely for Linux, Apple Mac OS X and Microsoft Windows.
  80. &lt;/p&gt;
  81. &lt;p&gt;
  82. The post goes on describing a new format, the
  83. &lt;a href="https://github.com/phoboslab/qop"&gt;&lt;code&gt;.QOP&lt;/code&gt;&lt;/a&gt;
  84. format which is very simple and has a few quirks that make it really nice to use
  85. for this scenario: file paths are hashed and the archive "header" (tailer?) is
  86. at the end of the file, unlike many other formats that store it at the start of
  87. the file. The hashing of file paths makes it easy to find files (see
  88. &lt;a href="https://en.wikipedia.org/wiki/Hash_table?useskin=monobook"&gt;hashmaps&lt;/a&gt;).
  89. The header being stored at the end makes it easy to locate without knowing the
  90. size of the archive. It's a great format! The tools around it aren't very well
  91. developed however and there's no formal specification to follow when
  92. implementing it, meaning that different implementations may have slight
  93. differences, making the creation of mods and extraction of assets harder.
  94. &lt;/p&gt;
  95. &lt;h2&gt;A better format&lt;/h2&gt;
  96. &lt;p&gt;
  97. I searched for the perfect file format for this use-case. Ideally one that does
  98. all the following:
  99. &lt;/p&gt;
  100. &lt;ul&gt;
  101. &lt;li&gt; simple format that's easy to read,&lt;/li&gt;
  102. &lt;li&gt; popular format with good tools,&lt;/li&gt;
  103. &lt;li&gt; free specification (or similar) describing the format in details,&lt;/li&gt;
  104. &lt;li&gt; ASCII (or compatible) encoding of file paths,&lt;/li&gt;
  105. &lt;li&gt; no decompression required,&lt;/li&gt;
  106. &lt;li&gt; header at the end of the file and&lt;/li&gt;
  107. &lt;li&gt; cryptographic hashing for file paths.&lt;/li&gt;
  108. &lt;/ul&gt;
  109. &lt;p&gt;
  110. After looking at the
  111. &lt;a href="https://en.wikipedia.org/wiki/Tar_%28computing%29?useskin=monobook"&gt;&lt;code&gt;.TAR&lt;/code&gt;&lt;/a&gt;,
  112. &lt;a href="https://en.wikipedia.org/wiki/Cpio?useskin=monobook"&gt;&lt;code&gt;.CPIO&lt;/code&gt;&lt;/a&gt;,
  113. &lt;a href="https://manpages.debian.org/trixie/binutils-common/ar.1"&gt;&lt;code&gt;.AR&lt;/code&gt;&lt;/a&gt;,
  114. &lt;a href="https://en.wikipedia.org/wiki/ZIP_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.ZIP&lt;/code&gt;&lt;/a&gt;,
  115. &lt;a href="https://en.wikipedia.org/wiki/RAR_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.RAR&lt;/code&gt;&lt;/a&gt;,
  116. &lt;a href="https://en.wikipedia.org/wiki/StuffIt?useskin=monobook"&gt;&lt;code&gt;.SIT&lt;/code&gt;&lt;/a&gt;,
  117. &lt;a href="https://en.wikipedia.org/wiki/ARC_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.ARC&lt;/code&gt;&lt;/a&gt;,
  118. &lt;a href="https://7-zip.org/7z.html"&gt;&lt;code&gt;.7Z&lt;/code&gt;&lt;/a&gt;,
  119. &lt;a href="https://en.wikipedia.org/wiki/Cabinet_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.CAB&lt;/code&gt;&lt;/a&gt;,
  120. &lt;a href="https://en.wikipedia.org/wiki/Windows_Imaging_Format?useskin=monobook"&gt;&lt;code&gt;.WIM&lt;/code&gt;&lt;/a&gt;
  121. and
  122. &lt;a href="https://en.wikipedia.org/wiki/ISO_9660?useskin=monobook"&gt;ISO 9660 (used by CD-ROM)&lt;/a&gt;,
  123. I've found that the only viable format is
  124. &lt;a href="https://en.wikipedia.org/wiki/ZIP_%28file_format%29?useskin=monobook"&gt;&lt;code&gt;.ZIP&lt;/code&gt;&lt;/a&gt;.
  125. The only thing that &lt;code&gt;.ZIP&lt;/code&gt; is missing is the hashing of file paths. The &lt;code&gt;.ZIP&lt;/code&gt;
  126. format supports archives stored only multiple diskettes so the creator of the
  127. format,
  128. &lt;a href="https://en.wikipedia.org/wiki/Phil_Katz?useskin=monobook"&gt;Phil Katz&lt;/a&gt;,
  129. decided to store the file index and archive header at the end of the archive so
  130. that adding files to the archive would only require inserting the last disc.
  131. This means we can concatenate our program and a &lt;code&gt;.ZIP&lt;/code&gt; archive and the result
  132. will readable by most tools. Some tools complain about the positions stored in
  133. the file index being offset. Example on Linux with
  134. &lt;a href="https://manpages.debian.org/trixie/unzip/unzip.1"&gt;&lt;code&gt;unzip(1)&lt;/code&gt;&lt;/a&gt;:
  135. &lt;/p&gt;
  136. &lt;pre&gt;
  137. $ unzip uberprogrammeArchive:  uberprogrammewarning [uberprogramme]:  15968 extra bytes at beginning or within zipfile  (attempting to process anyway) extracting: exemple.png$&lt;/pre&gt;
  138. &lt;p&gt;
  139. This isn't hard to fix, we'll see how later. The format also supports
  140. compression of individual files and storing information such as UNIX file mode
  141. bits. We can write a decoder that doesn't support these features, returning an
  142. error if it encounters them.
  143. &lt;/p&gt;
  144. &lt;h2&gt;Implementation&lt;/h2&gt;
  145. &lt;p&gt;
  146. The &lt;code&gt;.ZIP&lt;/code&gt; format is specified in a file called &lt;code&gt;APPNOTE.TXT&lt;/code&gt; published by
  147. &lt;a href="https://en.wikipedia.org/wiki/PKWare?useskin=monobook"&gt;PKWARE&lt;/a&gt;.
  148. There are many versions of this spec. I used
  149. &lt;a href="https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-1.0.txt"&gt;version 1.0&lt;/a&gt;
  150. because it's the first version and therefore the most compatible.
  151. &lt;br&gt;
  152. We start by reading the End of Central Directory (EOCD) record stored at the end
  153. of the archive. It looks like this:
  154. &lt;/p&gt;
  155. &lt;pre&gt;
  156. /* every number is read little endian */struct eocd { unsigned char signature[4]; /* always "PK\x05\x06" in ASCII */ uint16_t disc_number; /* always 0 for us */ uint16_t disc_number_of_index; /* always 0 for us */ uint16_t file_count_on_disc; uint16_t file_count_total; uint32_t index_size; uint32_t index_pos; uint16_t comment_len; /* always 0 for us */};&lt;/pre&gt;
  157. &lt;p&gt;
  158. The signature starts with the initials of Phil Katz, the creator of the format.
  159. &lt;code&gt;file_count_on_disc&lt;/code&gt; and &lt;code&gt;file_count_total&lt;/code&gt; need to have the same value because
  160. we don't support multiple discs.
  161. &lt;br&gt;
  162. We can now find the file index. &lt;code&gt;index_pos&lt;/code&gt; possibly contains incorrect
  163. information perhaps because some idiot decided to put an entire game's code
  164. before the archive contents. We can instead calculate the position of the index
  165. from the end of the file using &lt;code&gt;-(sizeof(struct eocd) - index_size)&lt;/code&gt;
  166. because (usually) the index is right before the EOCD record. We can then use the
  167. offset between &lt;code&gt;index_pos&lt;/code&gt; and the actual index position to know by how much the
  168. file is offset.
  169. &lt;br&gt;
  170. For programs like &lt;code&gt;unzip(1)&lt;/code&gt; to stop complaining about the archive being offset,
  171. we can update all the positions stored in the archive after concatenating.
  172. &lt;/p&gt;
  173. &lt;p&gt;
  174. We know where the index is, so we can read it now. Each entry in the index is a
  175. Central Directory File Header (CDFH) record, which looks like this:
  176. &lt;/p&gt;
  177. &lt;pre&gt;
  178. /* every number is read little endian */struct cdfh { unsigned char signature[4]; /* always "PK\x01\x02" in ASCII */ uint16_t version_created_by; /* can be ignored */ uint16_t version_to_extract; /* low byte &lt;= 10 */ uint16_t flags; /* always 0 for us */ uint16_t compression; /* always 0 for us */ uint16_t last_edit_time; /* can be ignored */ uint16_t last_edit_date; /* can be ignored */ uint32_t crc32; /* can be ignored */ uint32_t size_before_compression; uint32_t size_after_compression; uint16_t path_len; uint16_t extra_len; uint16_t comment_len; uint16_t disc_num; uint16_t internal_attributes; uint32_t external_attributes; uint32_t local_header_pos; /* relative to archive start */ /* this isn't valid C but it works for the sake of the example */ unsigned char path[path_len];};&lt;/pre&gt;
  179. &lt;p&gt;
  180. Note that the signature is different from the EOCD.
  181. &lt;br&gt;
  182. &lt;code&gt;version_to_extract&lt;/code&gt; is kinda weird, the low byte is the minimum format version
  183. to implement (up to 10 for version 1.0). The high byte is an OS identifier, 0
  184. represents Microsoft DOS for example. We can ignore the high byte because it's
  185. only useful to decode information we don't care about. Even if the archive was
  186. created on Microsoft DOS, Microsoft Windows or OS/2 which all use \ as a file
  187. path separator, the archive will use / as the file path separator.
  188. &lt;br&gt;
  189. &lt;code&gt;size_before_compression&lt;/code&gt; and &lt;code&gt;size_after_compression&lt;/code&gt; have the same value since
  190. we don't do compression. They store the file size in bytes.
  191. &lt;br&gt;
  192. The &lt;code&gt;path&lt;/code&gt; is encoded with
  193. &lt;a href="https://en.wikipedia.org/wiki/Code_page_437?useskin=monobook"&gt;IBM code page 437&lt;/a&gt;.
  194. This is the same ASCII-compatible encoding that the IBM PC and clones use. Newer
  195. versions of the format also support UTF-8 paths.
  196. &lt;/p&gt;
  197. &lt;p&gt;
  198. We're almost done, but we also need to deal with local file headers. This header
  199. is stored right before each file's content and doesn't contain anything really
  200. useful:
  201. &lt;/p&gt;
  202. &lt;pre&gt;
  203. /* every number is read little endian */struct lfh { unsigned char signature[4]; /* always "PK\x03\x04" in ASCII */ uint16_t version_to_extract; uint16_t flags; uint16_t compression; uint16_t last_edit_time; uint16_t last_edit_date; uint32_t crc32; uint32_t size_before_compression; uint32_t size_after_compression; uint16_t path_len; uint16_t extra_len; unsigned char path[path_len]; unsigned char extra[extra_len];};&lt;/pre&gt;
  204. &lt;p&gt;
  205. We can either read this header to ensure it contains the data we assume it does
  206. or decide to skip it entirely, which may cause issues if it contains incorrect
  207. data since this header has a variable length. The file contents are stored right
  208. after this header.
  209. &lt;/p&gt;
  210. &lt;h2&gt;Performance&lt;/h2&gt;
  211. &lt;p&gt;
  212. Even if &lt;code&gt;.ZIP&lt;/code&gt; is pretty simple, I wanted to compare its performance with
  213. &lt;code&gt;.QOP&lt;/code&gt;. I compared 3 different algorithms for reading the archive headers and
  214. 1 file from the archive:
  215. &lt;/p&gt;
  216. &lt;ul&gt;
  217. &lt;li&gt; qop.h,&lt;/li&gt;
  218. &lt;li&gt; a minimal &lt;code&gt;.ZIP&lt;/code&gt; implementation that skips the local file header and&lt;/li&gt;
  219. &lt;li&gt; a &lt;code&gt;.ZIP&lt;/code&gt; implementation that validates the local file header.&lt;/li&gt;
  220. &lt;/ul&gt;
  221. &lt;p&gt;
  222. These algorithms are so basic that on my test machine (AMD Ryzen 5 2600 with
  223. Arch Linux last updated on 13 September 2026), I needed tens of thousands of
  224. files for the difference to be significant enough. Using code compiled with
  225. &lt;code&gt;-O3&lt;/code&gt; on archives with 50,000 files, I got the following results:
  226. &lt;/p&gt;
  227. &lt;ul&gt;
  228. &lt;li&gt; 8.8ms ± 3.0ms for qop.h&lt;/li&gt;
  229. &lt;li&gt; 7.7ms ± 2.7ms for the minimal &lt;code&gt;.ZIP&lt;/code&gt;&lt;/li&gt;
  230. &lt;li&gt; 57.3ms ± 4.3ms for the &lt;code&gt;.ZIP&lt;/code&gt; that validates local file headers&lt;/li&gt;
  231. &lt;/ul&gt;
  232. &lt;p&gt;
  233. It's interesting to see that minimal &lt;code&gt;.ZIP&lt;/code&gt; is actually faster than &lt;code&gt;.QOP&lt;/code&gt;, but
  234. the difference is not very big. In real world scenarios, the real bottleneck
  235. would be the hard disc's reading speed anyway. I could also read the local file
  236. headers when reading each file instead of when opening the archive, but I don't
  237. know how to meaningfully compare this method with the other algorithms.
  238. &lt;/p&gt;
  239. &lt;p&gt;
  240. Using &lt;code&gt;.ZIP&lt;/code&gt; doesn't seem to have a major impact on performance. However, a
  241. &lt;code&gt;.ZIP&lt;/code&gt; file without compression is generally larger than a &lt;code&gt;.QOP&lt;/code&gt; because the
  242. headers are bigger and file paths are stored twice for each file. In my tests,
  243. the &lt;code&gt;.QOP&lt;/code&gt; archive is 4,488,902 bytes and the &lt;code&gt;.ZIP&lt;/code&gt; is almost twice the size
  244. with 7,477,802 bytes.
  245. &lt;/p&gt;
  246. &lt;p&gt;
  247. My testing code is available using
  248. &lt;a href="http://doclic.eu/ftp/zip_qop_bench.tar.gz"&gt;HTTP&lt;/a&gt;
  249. and using
  250. &lt;a href="ftp://ftp@doclic.eu/pub/zip_qop_bench.tar.gz"&gt;FTP&lt;/a&gt;.
  251. &lt;/p&gt;
  252. &lt;h2&gt;Closing thoughts&lt;/h2&gt;
  253. &lt;p&gt;
  254. The &lt;code&gt;.ZIP&lt;/code&gt; format is not a strictly superior or inferior format when compared to
  255. &lt;code&gt;.QOP&lt;/code&gt;. Each format has its trade-offs, &lt;code&gt;.ZIP&lt;/code&gt; having more flexibility, better
  256. tools and better support by third-party programs, but &lt;code&gt;.QOP&lt;/code&gt; is simpler and
  257. smaller. There is no single best format for every use-case, you should choose
  258. according to what fits your needs best.
  259. &lt;/p&gt;
  260. </description>
  261. </item>
  262. </channel>
  263. </rss>
  264.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.rssboard.org/rss-validator/check.cgi?url=http%3A//doclic.eu/blog/rss-en.xml

Software created by Sam Ruby, Mark Pilgrim, Joseph Walton and Phil Ringnalda