<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zero abstraction</title>
    <description>The latest articles on DEV Community by zero abstraction (@zeroabs_1c2ccb75675).</description>
    <link>https://dev.to/zeroabs_1c2ccb75675</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4095076%2Fcf4dba15-4811-4127-a5de-e7d7ed58ae7e.png</url>
      <title>DEV Community: zero abstraction</title>
      <link>https://dev.to/zeroabs_1c2ccb75675</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeroabs_1c2ccb75675"/>
    <language>en</language>
    <item>
      <title>I built a file format in c/c++</title>
      <dc:creator>zero abstraction</dc:creator>
      <pubDate>Wed, 26 Aug 2026 05:34:10 +0000</pubDate>
      <link>https://dev.to/zeroabs_1c2ccb75675/i-built-a-file-format-in-cc-2i15</link>
      <guid>https://dev.to/zeroabs_1c2ccb75675/i-built-a-file-format-in-cc-2i15</guid>
      <description>&lt;h1&gt;
  
  
  I Built a File Format From Scratch in C/C++
&lt;/h1&gt;

&lt;p&gt;A few days ago, I realized something slightly embarrassing.&lt;/p&gt;

&lt;p&gt;I had been working with files my entire life, but I had never really stopped to ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually is a file?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I knew that a &lt;code&gt;.txt&lt;/code&gt; file contains text. I knew that images, videos, executables, and documents all have different formats.&lt;/p&gt;

&lt;p&gt;But if I wanted to invent my own file format from scratch, what would I actually have to build?&lt;/p&gt;

&lt;p&gt;So I decided to find out.&lt;/p&gt;

&lt;p&gt;I built my own binary container format in C/C++.&lt;/p&gt;

&lt;p&gt;I called it &lt;strong&gt;ZAF&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And along the way, I ended up learning far more about files, bytes, offsets, metadata, directory structures, and binary parsing than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;p&gt;Take a directory containing arbitrary files and turn the whole thing into a single &lt;code&gt;.zaf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;For example, suppose I have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── hello.txt
├── image.png
└── src/
    └── main.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted to be able to turn that into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project.zaf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and later give that &lt;code&gt;.zaf&lt;/code&gt; file to a reader program and get the original directory back.&lt;/p&gt;

&lt;p&gt;That immediately created a much bigger question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you actually store all of this information inside one binary file?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I needed some kind of structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing the format
&lt;/h2&gt;

&lt;p&gt;I eventually settled on a structure roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metadata
    ↓
file records
    ↓
offsets
    ↓
actual file bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beginning of the ZAF file contains information about the files stored inside it.&lt;/p&gt;

&lt;p&gt;Each file record contains things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file name&lt;/li&gt;
&lt;li&gt;path&lt;/li&gt;
&lt;li&gt;size&lt;/li&gt;
&lt;li&gt;offset&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The offset tells the reader where the actual bytes belonging to that file begin.&lt;/p&gt;

&lt;p&gt;So if I had three files, the format could conceptually look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────┐
│ Magic bytes              │
├──────────────────────────┤
│ Version                  │
├──────────────────────────┤
│ Directory information    │
├──────────────────────────┤
│ File metadata             │
├──────────────────────────┤
│ File metadata             │
├──────────────────────────┤
│ File metadata             │
├──────────────────────────┤
│ Actual file bytes         │
├──────────────────────────┤
│ Actual file bytes         │
├──────────────────────────┤
│ Actual file bytes         │
└──────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sounds straightforward.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  The magic bytes
&lt;/h2&gt;

&lt;p&gt;The first thing the reader needs to know is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Is this actually a ZAF file?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I gave ZAF a magic-number header.&lt;/p&gt;

&lt;p&gt;The reader starts by checking the first six bytes.&lt;/p&gt;

&lt;p&gt;If they don't match the expected header, the reader knows that it isn't looking at a valid ZAF file.&lt;/p&gt;

&lt;p&gt;This is one of those concepts that sounds incredibly simple until you realize that you are now manually defining exactly what every byte in your format means.&lt;/p&gt;

&lt;p&gt;There is no operating system specification telling you what to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You are the specification.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Storing directories
&lt;/h2&gt;

&lt;p&gt;The next problem was the directory structure.&lt;/p&gt;

&lt;p&gt;A real directory isn't just a list of filenames.&lt;/p&gt;

&lt;p&gt;It can contain nested directories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── assets/
│   ├── image.png
│   └── logo.png
└── src/
    ├── main.cpp
    └── utils.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I needed to traverse the directory recursively.&lt;/p&gt;

&lt;p&gt;I used C++'s &lt;code&gt;std::filesystem&lt;/code&gt; to walk through the directory structure.&lt;/p&gt;

&lt;p&gt;The traversal essentially became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;directory
   ↓
find entry
   ↓
is it a directory?
   ├── yes → enter it
   └── no  → record the file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A depth-first traversal worked nicely for this.&lt;/p&gt;

&lt;p&gt;For every file I encountered, I collected the information I would eventually need to reconstruct it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metadata and offsets
&lt;/h2&gt;

&lt;p&gt;Now came one of the most important parts of the format.&lt;/p&gt;

&lt;p&gt;Suppose I have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a.txt → 100 bytes
b.txt → 250 bytes
c.txt → 80 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the data for &lt;code&gt;a.txt&lt;/code&gt; starts at offset &lt;code&gt;X&lt;/code&gt;, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a.txt → X
b.txt → X + 100
c.txt → X + 100 + 250
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The offset isn't the file's data.&lt;/p&gt;

&lt;p&gt;It is simply the position at which the reader should look for that data.&lt;/p&gt;

&lt;p&gt;This gives the reader a way to jump directly to the appropriate section of the ZAF file.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metadata
    |
    | "b.txt is 250 bytes"
    | "b.txt starts at offset 1234"
    ↓
seek to byte 1234
    ↓
read 250 bytes
    ↓
write b.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And suddenly I was no longer just "saving files."&lt;/p&gt;

&lt;p&gt;I was designing a binary data structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Knowing when a section ends
&lt;/h2&gt;

&lt;p&gt;Another problem appeared.&lt;/p&gt;

&lt;p&gt;The reader needs to know where the directory information ends and where the next section begins.&lt;/p&gt;

&lt;p&gt;I used a dedicated termination marker made from three bytes.&lt;/p&gt;

&lt;p&gt;So instead of the reader blindly reading until some arbitrary position, it could essentially do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read data
    ↓
is this the termination marker?
    ├── no  → continue
    └── yes → section finished
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used the same general idea when parsing the relevant metadata.&lt;/p&gt;

&lt;p&gt;It isn't the only way to design a format, but for a small experimental format it made the structure easy to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then I had to build the reader
&lt;/h2&gt;

&lt;p&gt;Writing the file was only half the problem.&lt;/p&gt;

&lt;p&gt;A format is useless if you can create it but can't read it.&lt;/p&gt;

&lt;p&gt;So I wrote a reader that had to reverse the process.&lt;/p&gt;

&lt;p&gt;It needed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the magic bytes.&lt;/li&gt;
&lt;li&gt;Read the version.&lt;/li&gt;
&lt;li&gt;Parse the directory information.&lt;/li&gt;
&lt;li&gt;Parse the file metadata.&lt;/li&gt;
&lt;li&gt;Find the offsets.&lt;/li&gt;
&lt;li&gt;Jump to the appropriate locations.&lt;/li&gt;
&lt;li&gt;Read the file bytes.&lt;/li&gt;
&lt;li&gt;Recreate the original directory structure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.zaf
 ↓
parse
 ↓
metadata
 ↓
offsets
 ↓
file bytes
 ↓
reconstructed files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I thought I was basically done.&lt;/p&gt;

&lt;p&gt;I was very wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Everything was corrupted
&lt;/h2&gt;

&lt;p&gt;I ran the reader.&lt;/p&gt;

&lt;p&gt;The directory appeared.&lt;/p&gt;

&lt;p&gt;The files appeared.&lt;/p&gt;

&lt;p&gt;And then I opened them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They were corrupted.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was one of the most useful bugs I encountered during the entire project.&lt;/p&gt;

&lt;p&gt;The format itself looked reasonable.&lt;/p&gt;

&lt;p&gt;The metadata looked reasonable.&lt;/p&gt;

&lt;p&gt;The offsets looked reasonable.&lt;/p&gt;

&lt;p&gt;So where was the problem?&lt;/p&gt;

&lt;p&gt;I went back through the reader.&lt;/p&gt;

&lt;p&gt;Eventually I found it.&lt;/p&gt;

&lt;p&gt;The reader was starting the first file &lt;strong&gt;three bytes too early&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those three bytes were part of the structure that came immediately before the actual file data.&lt;/p&gt;

&lt;p&gt;So instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[metadata][actual file data]
                  ↑
              start here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the reader was effectively doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[metadata][actual file data]
       ↑
   start here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was corrupted output.&lt;/p&gt;

&lt;p&gt;And this was exactly the kind of bug I had hoped this project would force me to understand.&lt;/p&gt;

&lt;p&gt;When you work with high-level abstractions, you can sometimes forget that the computer is ultimately just moving bytes around.&lt;/p&gt;

&lt;p&gt;Here, there was nowhere to hide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three wrong bytes were enough to break the entire file.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I fixed the offset calculation.&lt;/p&gt;

&lt;p&gt;Ran the reader again.&lt;/p&gt;

&lt;p&gt;And this time, the files came back correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What building ZAF taught me
&lt;/h2&gt;

&lt;p&gt;The biggest thing I learned wasn't actually how to create a file extension.&lt;/p&gt;

&lt;p&gt;It was how much structure exists underneath something as ordinary as a file.&lt;/p&gt;

&lt;p&gt;Before this project, words like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;magic bytes&lt;/li&gt;
&lt;li&gt;offsets&lt;/li&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;binary layout&lt;/li&gt;
&lt;li&gt;parsing&lt;/li&gt;
&lt;li&gt;file records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;felt like disconnected technical vocabulary.&lt;/p&gt;

&lt;p&gt;After building ZAF, they became concrete.&lt;/p&gt;

&lt;p&gt;I had to decide:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does byte 0 mean?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does byte 1 mean?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does the reader know where one section ends?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it find a particular file?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it reconstruct a nested directory?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those aren't abstract questions anymore.&lt;/p&gt;

&lt;p&gt;They are design decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The final structure
&lt;/h2&gt;

&lt;p&gt;The final ZAF file ended up following a structure along these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────┐
│ Magic bytes               │
├───────────────────────────┤
│ Version                   │
├───────────────────────────┤
│ Directory information     │
├───────────────────────────┤
│ File records              │
├───────────────────────────┤
│ Termination marker        │
├───────────────────────────┤
│ Actual file bytes         │
└───────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's certainly not intended to replace established formats like ZIP or TAR.&lt;/p&gt;

&lt;p&gt;That wasn't the point.&lt;/p&gt;

&lt;p&gt;The point was to understand what it actually takes to design one.&lt;/p&gt;

&lt;p&gt;And after several days of debugging, experimenting, and staring at bytes that looked completely meaningless at first...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I had a working file format.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I think this is the part I found most interesting.&lt;/p&gt;

&lt;p&gt;You don't always need to build something because it is commercially useful.&lt;/p&gt;

&lt;p&gt;Sometimes building something useless is exactly what teaches you how useful things work.&lt;/p&gt;

&lt;p&gt;I didn't need another file format.&lt;/p&gt;

&lt;p&gt;I wanted to understand one.&lt;/p&gt;

&lt;p&gt;So I built one.&lt;/p&gt;

&lt;p&gt;And somewhere between calculating offsets, traversing directories, reading raw bytes, and hunting down a three-byte bug, the whole subject became much less abstract.&lt;/p&gt;

&lt;p&gt;That's the reason I call myself&amp;nbsp;&amp;nbsp;&lt;strong&gt;Zero Abstraction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes the best way to understand what's underneath an abstraction is to remove the abstraction entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The complete project is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/zero-abstraction/project_zaf" rel="noopener noreferrer"&gt;ZAF — source code&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to see the entire process as a video, I've also documented the build from beginning to end on YouTube:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=Y7Yr9n2ne7s" rel="noopener noreferrer"&gt;Optional: Watch the build on YouTube&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code is the important part, though.&lt;/p&gt;

&lt;p&gt;Feel free to inspect it, break it, improve it, or tell me how badly I reinvented the wheel. 😄&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I used AI tools as an assistant while working on this article, including for organizing and polishing the writing. The project, implementation, debugging process, and technical experience described here are my own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>programming</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
