File: datauri.go
   1 /*
   2 The MIT License (MIT)
   3 
   4 Copyright (c) 2026 pacman64
   5 
   6 Permission is hereby granted, free of charge, to any person obtaining a copy of
   7 this software and associated documentation files (the "Software"), to deal
   8 in the Software without restriction, including without limitation the rights to
   9 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  10 of the Software, and to permit persons to whom the Software is furnished to do
  11 so, subject to the following conditions:
  12 
  13 The above copyright notice and this permission notice shall be included in all
  14 copies or substantial portions of the Software.
  15 
  16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22 SOFTWARE.
  23 */
  24 
  25 /*
  26 To compile a smaller-sized command-line app, you can use the `go` command as
  27 follows:
  28 
  29 go build -ldflags "-s -w" -trimpath datauri.go
  30 */
  31 
  32 package main
  33 
  34 import (
  35     "bufio"
  36     "bytes"
  37     "encoding/base64"
  38     "errors"
  39     "io"
  40     "os"
  41     "strings"
  42 )
  43 
  44 const info = `
  45 datauri [options...] [filenames...]
  46 
  47 
  48 Encode bytes as data-URIs, auto-detecting the file/data type using the first
  49 few bytes from each data/file stream. When given multiple inputs, the output
  50 will be multiple lines, one for each file given.
  51 
  52 Empty files/inputs result in empty lines. A simple dash (-) stands for the
  53 standard-input, which is also used automatically when not given any files.
  54 
  55 Data-URIs are base64-encoded text representations of arbitrary data, which
  56 include their payload's MIME-type, and which are directly useable/shareable
  57 in web-browsers as links, despite not looking like normal links/URIs.
  58 
  59 Some web-browsers limit the size of handled data-URIs to tens of kilobytes.
  60 
  61 
  62 Options
  63 
  64     -h, -help, --h, --help              show this help message
  65 `
  66 
  67 func main() {
  68     if len(os.Args) > 1 {
  69         switch os.Args[1] {
  70         case `-h`, `--h`, `-help`, `--help`:
  71             os.Stdout.WriteString(info[1:])
  72             return
  73         }
  74     }
  75 
  76     if err := run(os.Stdout, os.Args[1:]); err != nil && err != io.EOF {
  77         os.Stderr.WriteString(err.Error())
  78         os.Stderr.WriteString("\n")
  79         os.Exit(1)
  80     }
  81 }
  82 
  83 func run(w io.Writer, args []string) error {
  84     bw := bufio.NewWriter(w)
  85     defer bw.Flush()
  86 
  87     if len(args) == 0 {
  88         return dataURI(bw, os.Stdin, `<stdin>`)
  89     }
  90 
  91     for _, name := range args {
  92         if err := handleFile(bw, name); err != nil {
  93             return err
  94         }
  95     }
  96     return nil
  97 }
  98 
  99 func handleFile(w *bufio.Writer, name string) error {
 100     if name == `` || name == `-` {
 101         return dataURI(w, os.Stdin, `<stdin>`)
 102     }
 103 
 104     f, err := os.Open(name)
 105     if err != nil {
 106         return errors.New(`can't read from file named "` + name + `"`)
 107     }
 108     defer f.Close()
 109 
 110     return dataURI(w, f, name)
 111 }
 112 
 113 func dataURI(w *bufio.Writer, r io.Reader, name string) error {
 114     var buf [64]byte
 115     n, err := r.Read(buf[:])
 116     if err != nil && err != io.EOF {
 117         return err
 118     }
 119     start := buf[:n]
 120 
 121     // handle regular data, trying to auto-detect its MIME type using
 122     // its first few bytes
 123     mime, ok := detectMIME(start)
 124     if !ok {
 125         return errors.New(name + `: unknown file type`)
 126     }
 127 
 128     w.WriteString(`data:`)
 129     w.WriteString(mime)
 130     w.WriteString(`;base64,`)
 131     r = io.MultiReader(bytes.NewReader(start), r)
 132     enc := base64.NewEncoder(base64.StdEncoding, w)
 133     if _, err := io.Copy(enc, r); err != nil {
 134         return err
 135     }
 136     enc.Close()
 137 
 138     w.WriteByte('\n')
 139     if err := w.Flush(); err != nil {
 140         return io.EOF
 141     }
 142     return nil
 143 }
 144 
 145 // makeDotless is similar to filepath.Ext, except its results never start
 146 // with a dot
 147 func makeDotless(s string) string {
 148     i := strings.LastIndexByte(s, '.')
 149     if i >= 0 {
 150         return s[(i + 1):]
 151     }
 152     return s
 153 }
 154 
 155 // hasPrefixByte is a simpler, single-byte version of bytes.HasPrefix
 156 func hasPrefixByte(b []byte, prefix byte) bool {
 157     return len(b) > 0 && b[0] == prefix
 158 }
 159 
 160 // hasPrefixFold is a case-insensitive bytes.HasPrefix
 161 func hasPrefixFold(s []byte, prefix []byte) bool {
 162     n := len(prefix)
 163     return len(s) >= n && bytes.EqualFold(s[:n], prefix)
 164 }
 165 
 166 // trimLeadingWhitespace ignores leading space-like symbols: this is useful
 167 // to handle text-based data formats more flexibly
 168 func trimLeadingWhitespace(b []byte) []byte {
 169     for len(b) > 0 {
 170         switch b[0] {
 171         case ' ', '\t', '\n', '\r':
 172             b = b[1:]
 173         default:
 174             return b
 175         }
 176     }
 177 
 178     // an empty slice is all that's left, at this point
 179     return nil
 180 }
 181 
 182 // nameToMIME tries to match a MIME type to a filename, dotted file extension,
 183 // or a dot-less filetype/extension given
 184 func nameToMIME(fname string) (mimeType string, ok bool) {
 185     // handle dotless file types and filenames alike
 186     kind, ok := type2mime[makeDotless(fname)]
 187     return kind, ok
 188 }
 189 
 190 // detectMIME guesses the first appropriate MIME type from the first few
 191 // data bytes given: 24 bytes are enough to detect all supported types
 192 func detectMIME(b []byte) (mimeType string, ok bool) {
 193     t, ok := detectType(b)
 194     if ok {
 195         return t, true
 196     }
 197     return ``, false
 198 }
 199 
 200 // detectType guesses the first appropriate file type for the data given:
 201 // here the type is a a filename extension without the leading dot
 202 func detectType(b []byte) (dotlessExt string, ok bool) {
 203     // empty data, so there's no way to detect anything
 204     if len(b) == 0 {
 205         return ``, false
 206     }
 207 
 208     // check for plain-text web-document formats case-insensitively
 209     kind, ok := checkDoc(b)
 210     if ok {
 211         return kind, true
 212     }
 213 
 214     // check data formats which allow any byte at the start
 215     kind, ok = checkSpecial(b)
 216     if ok {
 217         return kind, true
 218     }
 219 
 220     // check all other supported data formats
 221     headers := hdrDispatch[b[0]]
 222     for _, t := range headers {
 223         if hasPrefixPattern(b[1:], t.Header[1:], cba) {
 224             return t.Type, true
 225         }
 226     }
 227 
 228     // unrecognized data format
 229     return ``, false
 230 }
 231 
 232 // checkDoc tries to guess if the bytes given are the start of HTML, SVG,
 233 // XML, or JSON data
 234 func checkDoc(b []byte) (kind string, ok bool) {
 235     // ignore leading whitespaces
 236     b = trimLeadingWhitespace(b)
 237 
 238     // can't detect anything with empty data
 239     if len(b) == 0 {
 240         return ``, false
 241     }
 242 
 243     // handle XHTML documents which don't start with a doctype declaration
 244     if bytes.Contains(b, doctypeHTML) {
 245         return html, true
 246     }
 247 
 248     // handle HTML/SVG/XML documents
 249     if hasPrefixByte(b, '<') {
 250         if hasPrefixFold(b, []byte{'<', '?', 'x', 'm', 'l'}) {
 251             if bytes.Contains(b, []byte{'<', 's', 'v', 'g'}) {
 252                 return svg, true
 253             }
 254             return xml, true
 255         }
 256 
 257         headers := hdrDispatch['<']
 258         for _, v := range headers {
 259             if hasPrefixFold(b, v.Header) {
 260                 return v.Type, true
 261             }
 262         }
 263         return ``, false
 264     }
 265 
 266     // handle JSON with top-level arrays
 267     if hasPrefixByte(b, '[') {
 268         // match [", or [[, or [{, ignoring spaces between
 269         b = trimLeadingWhitespace(b[1:])
 270         if len(b) > 0 {
 271             switch b[0] {
 272             case '"', '[', '{':
 273                 return json, true
 274             }
 275         }
 276         return ``, false
 277     }
 278 
 279     // handle JSON with top-level objects
 280     if hasPrefixByte(b, '{') {
 281         // match {", ignoring spaces between: after {, the only valid syntax
 282         // which can follow is the opening quote for the expected object-key
 283         b = trimLeadingWhitespace(b[1:])
 284         if hasPrefixByte(b, '"') {
 285             return json, true
 286         }
 287         return ``, false
 288     }
 289 
 290     // checking for a quoted string, any of the JSON keywords, or even a
 291     // number seems too ambiguous to declare the data valid JSON
 292 
 293     // no web-document format detected
 294     return ``, false
 295 }
 296 
 297 // checkSpecial handles special file-format headers, which should be checked
 298 // before the normal file-type headers, since the first-byte dispatch algo
 299 // doesn't work for these
 300 func checkSpecial(b []byte) (kind string, ok bool) {
 301     if len(b) >= 8 && bytes.Index(b, []byte{'f', 't', 'y', 'p'}) == 4 {
 302         for _, t := range specialHeaders {
 303             if hasPrefixPattern(b[4:], t.Header[4:], cba) {
 304                 return t.Type, true
 305             }
 306         }
 307     }
 308     return ``, false
 309 }
 310 
 311 // hasPrefixPattern works like bytes.HasPrefix, except it allows for a special
 312 // value to signal any byte is allowed on specific spots
 313 func hasPrefixPattern(what []byte, pat []byte, wildcard byte) bool {
 314     // if the data are shorter than the pattern to match, there's no match
 315     if len(what) < len(pat) {
 316         return false
 317     }
 318 
 319     // use a slice which ensures the pattern length is never exceeded
 320     what = what[:len(pat)]
 321 
 322     for i, x := range what {
 323         y := pat[i]
 324         if x != y && y != wildcard {
 325             return false
 326         }
 327     }
 328     return true
 329 }
 330 
 331 // all the MIME types used/recognized in this package
 332 const (
 333     aiff    = `audio/aiff`
 334     au      = `audio/basic`
 335     avi     = `video/avi`
 336     avif    = `image/avif`
 337     bmp     = `image/x-bmp`
 338     caf     = `audio/x-caf`
 339     cur     = `image/vnd.microsoft.icon`
 340     css     = `text/css`
 341     csv     = `text/csv`
 342     djvu    = `image/x-djvu`
 343     elf     = `application/x-elf`
 344     exe     = `application/vnd.microsoft.portable-executable`
 345     flac    = `audio/x-flac`
 346     gif     = `image/gif`
 347     gz      = `application/gzip`
 348     heic    = `image/heic`
 349     htm     = `text/html`
 350     html    = `text/html`
 351     ico     = `image/x-icon`
 352     iso     = `application/octet-stream`
 353     jpg     = `image/jpeg`
 354     jpeg    = `image/jpeg`
 355     js      = `application/javascript`
 356     json    = `application/json`
 357     m4a     = `audio/aac`
 358     m4v     = `video/x-m4v`
 359     mid     = `audio/midi`
 360     mov     = `video/quicktime`
 361     mp4     = `video/mp4`
 362     mp3     = `audio/mpeg`
 363     mpg     = `video/mpeg`
 364     ogg     = `audio/ogg`
 365     opus    = `audio/opus`
 366     pdf     = `application/pdf`
 367     png     = `image/png`
 368     ps      = `application/postscript`
 369     psd     = `image/vnd.adobe.photoshop`
 370     rtf     = `application/rtf`
 371     sqlite3 = `application/x-sqlite3`
 372     svg     = `image/svg+xml`
 373     text    = `text/plain`
 374     tiff    = `image/tiff`
 375     tsv     = `text/tsv`
 376     wasm    = `application/wasm`
 377     wav     = `audio/x-wav`
 378     webp    = `image/webp`
 379     webm    = `video/webm`
 380     xml     = `application/xml`
 381     zip     = `application/zip`
 382     zst     = `application/zstd`
 383 )
 384 
 385 // type2mime turns dotless format-names into MIME types
 386 var type2mime = map[string]string{
 387     `aiff`:    aiff,
 388     `wav`:     wav,
 389     `avi`:     avi,
 390     `jpg`:     jpg,
 391     `jpeg`:    jpeg,
 392     `m4a`:     m4a,
 393     `mp4`:     mp4,
 394     `m4v`:     m4v,
 395     `mov`:     mov,
 396     `png`:     png,
 397     `avif`:    avif,
 398     `webp`:    webp,
 399     `gif`:     gif,
 400     `tiff`:    tiff,
 401     `psd`:     psd,
 402     `flac`:    flac,
 403     `webm`:    webm,
 404     `mpg`:     mpg,
 405     `zip`:     zip,
 406     `gz`:      gz,
 407     `zst`:     zst,
 408     `mp3`:     mp3,
 409     `opus`:    opus,
 410     `bmp`:     bmp,
 411     `mid`:     mid,
 412     `ogg`:     ogg,
 413     `html`:    html,
 414     `htm`:     htm,
 415     `svg`:     svg,
 416     `xml`:     xml,
 417     `rtf`:     rtf,
 418     `pdf`:     pdf,
 419     `ps`:      ps,
 420     `au`:      au,
 421     `ico`:     ico,
 422     `cur`:     cur,
 423     `caf`:     caf,
 424     `heic`:    heic,
 425     `sqlite3`: sqlite3,
 426     `elf`:     elf,
 427     `exe`:     exe,
 428     `wasm`:    wasm,
 429     `iso`:     iso,
 430     `txt`:     text,
 431     `css`:     css,
 432     `csv`:     csv,
 433     `tsv`:     tsv,
 434     `js`:      js,
 435     `json`:    json,
 436     `geojson`: json,
 437 }
 438 
 439 // formatDescriptor ties a file-header pattern to its data-format type
 440 type formatDescriptor struct {
 441     Header []byte
 442     Type   string
 443 }
 444 
 445 // can be anything: ensure this value differs from all other literal bytes
 446 // in the generic-headers table: failing that, its value could cause subtle
 447 // type-misdetection bugs
 448 const cba = 0xFD // 253, which is > 127, the highest-valued ascii symbol
 449 
 450 // dash-streamed m4a format
 451 var m4aDash = []byte{
 452     cba, cba, cba, cba, 'f', 't', 'y', 'p', 'd', 'a', 's', 'h',
 453     000, 000, 000, 000, 'i', 's', 'o', '6', 'm', 'p', '4', '1',
 454 }
 455 
 456 // format markers with leading wildcards, which should be checked before the
 457 // normal ones: this is to prevent mismatches with the latter types, even
 458 // though you can make probabilistic arguments which suggest these mismatches
 459 // should be very unlikely in practice
 460 var specialHeaders = []formatDescriptor{
 461     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', '4', 'A', ' '}, m4a},
 462     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', '4', 'A', 000}, m4a},
 463     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'M', 'S', 'N', 'V'}, mp4},
 464     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm'}, mp4},
 465     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'm', 'p', '4', '2'}, m4v},
 466     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'q', 't', ' ', ' '}, mov},
 467     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'h', 'e', 'i', 'c'}, heic},
 468     {[]byte{cba, cba, cba, cba, 'f', 't', 'y', 'p', 'a', 'v', 'i', 'f'}, avif},
 469     {m4aDash, m4a},
 470 }
 471 
 472 // sqlite3 database format
 473 var sqlite3db = []byte{
 474     'S', 'Q', 'L', 'i', 't', 'e', ' ',
 475     'f', 'o', 'r', 'm', 'a', 't', ' ', '3',
 476     000,
 477 }
 478 
 479 // windows-variant bitmap file-header, which is followed by a byte-counter for
 480 // the 40-byte infoheader which follows that
 481 var winbmp = []byte{
 482     'B', 'M', cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, cba, 40,
 483 }
 484 
 485 // deja-vu document format
 486 var djv = []byte{
 487     'A', 'T', '&', 'T', 'F', 'O', 'R', 'M', cba, cba, cba, cba, 'D', 'J', 'V',
 488 }
 489 
 490 var doctypeHTML = []byte{
 491     '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E', ' ', 'h', 't', 'm', 'l',
 492 }
 493 
 494 // hdrDispatch groups format-description-groups by their first byte, thus
 495 // shortening total lookups for some data header: notice how the `ftyp` data
 496 // formats aren't handled here, since these can start with any byte, instead
 497 // of the literal value of the any-byte markers they use
 498 var hdrDispatch = [256][]formatDescriptor{
 499     {
 500         {[]byte{000, 000, 001, 0xBA}, mpg},
 501         {[]byte{000, 000, 001, 0xB3}, mpg},
 502         {[]byte{000, 000, 001, 000}, ico},
 503         {[]byte{000, 000, 002, 000}, cur},
 504         {[]byte{000, 'a', 's', 'm'}, wasm},
 505     }, // 0
 506     nil, // 1
 507     nil, // 2
 508     nil, // 3
 509     nil, // 4
 510     nil, // 5
 511     nil, // 6
 512     nil, // 7
 513     nil, // 8
 514     nil, // 9
 515     nil, // 10
 516     nil, // 11
 517     nil, // 12
 518     nil, // 13
 519     nil, // 14
 520     nil, // 15
 521     nil, // 16
 522     nil, // 17
 523     nil, // 18
 524     nil, // 19
 525     nil, // 20
 526     nil, // 21
 527     nil, // 22
 528     nil, // 23
 529     nil, // 24
 530     nil, // 25
 531     {
 532         {[]byte{0x1A, 0x45, 0xDF, 0xA3}, webm},
 533     }, // 26
 534     nil, // 27
 535     nil, // 28
 536     nil, // 29
 537     nil, // 30
 538     {
 539         // {[]byte{0x1F, 0x8B, 0x08, 0x08}, gz},
 540         {[]byte{0x1F, 0x8B, 0x08}, gz},
 541     }, // 31
 542     nil, // 32
 543     nil, // 33 !
 544     nil, // 34 "
 545     {
 546         {[]byte{'#', '!', ' '}, text},
 547         {[]byte{'#', '!', '/'}, text},
 548     }, // 35 #
 549     nil, // 36 $
 550     {
 551         {[]byte{'%', 'P', 'D', 'F'}, pdf},
 552         {[]byte{'%', '!', 'P', 'S'}, ps},
 553     }, // 37 %
 554     nil, // 38 &
 555     nil, // 39 '
 556     {
 557         {[]byte{0x28, 0xB5, 0x2F, 0xFD}, zst},
 558     }, // 40 (
 559     nil, // 41 )
 560     nil, // 42 *
 561     nil, // 43 +
 562     nil, // 44 ,
 563     nil, // 45 -
 564     {
 565         {[]byte{'.', 's', 'n', 'd'}, au},
 566     }, // 46 .
 567     nil, // 47 /
 568     nil, // 48 0
 569     nil, // 49 1
 570     nil, // 50 2
 571     nil, // 51 3
 572     nil, // 52 4
 573     nil, // 53 5
 574     nil, // 54 6
 575     nil, // 55 7
 576     {
 577         {[]byte{'8', 'B', 'P', 'S'}, psd},
 578     }, // 56 8
 579     nil, // 57 9
 580     nil, // 58 :
 581     nil, // 59 ;
 582     {
 583         // func checkDoc is better for these, since it's case-insensitive
 584         {doctypeHTML, html},
 585         {[]byte{'<', 's', 'v', 'g'}, svg},
 586         {[]byte{'<', 'h', 't', 'm', 'l', '>'}, html},
 587         {[]byte{'<', 'h', 'e', 'a', 'd', '>'}, html},
 588         {[]byte{'<', 'b', 'o', 'd', 'y', '>'}, html},
 589         {[]byte{'<', '?', 'x', 'm', 'l'}, xml},
 590     }, // 60 <
 591     nil, // 61 =
 592     nil, // 62 >
 593     nil, // 63 ?
 594     nil, // 64 @
 595     {
 596         {djv, djvu},
 597     }, // 65 A
 598     {
 599         {winbmp, bmp},
 600     }, // 66 B
 601     nil, // 67 C
 602     nil, // 68 D
 603     nil, // 69 E
 604     {
 605         {[]byte{'F', 'O', 'R', 'M', cba, cba, cba, cba, 'A', 'I', 'F', 'F'}, aiff},
 606         {[]byte{'F', 'O', 'R', 'M', cba, cba, cba, cba, 'A', 'I', 'F', 'C'}, aiff},
 607     }, // 70 F
 608     {
 609         {[]byte{'G', 'I', 'F', '8', '7', 'a'}, gif},
 610         {[]byte{'G', 'I', 'F', '8', '9', 'a'}, gif},
 611     }, // 71 G
 612     nil, // 72 H
 613     {
 614         {[]byte{'I', 'D', '3', 2}, mp3}, // ID3-format metadata
 615         {[]byte{'I', 'D', '3', 3}, mp3}, // ID3-format metadata
 616         {[]byte{'I', 'D', '3', 4}, mp3}, // ID3-format metadata
 617         {[]byte{'I', 'I', '*', 000}, tiff},
 618     }, // 73 I
 619     nil, // 74 J
 620     nil, // 75 K
 621     nil, // 76 L
 622     {
 623         {[]byte{'M', 'M', 000, '*'}, tiff},
 624         {[]byte{'M', 'T', 'h', 'd'}, mid},
 625         {[]byte{'M', 'Z', cba, 000, cba, 000}, exe},
 626         // {[]byte{'M', 'Z', 0x90, 000, 003, 000}, exe},
 627         // {[]byte{'M', 'Z', 0x78, 000, 001, 000}, exe},
 628         // {[]byte{'M', 'Z', 'P', 000, 002, 000}, exe},
 629     }, // 77 M
 630     nil, // 78 N
 631     {
 632         {[]byte{'O', 'g', 'g', 'S'}, ogg},
 633     }, // 79 O
 634     {
 635         {[]byte{'P', 'K', 003, 004}, zip},
 636     }, // 80 P
 637     nil, // 81 Q
 638     {
 639         {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'W', 'E', 'B', 'P'}, webp},
 640         {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'W', 'A', 'V', 'E'}, wav},
 641         {[]byte{'R', 'I', 'F', 'F', cba, cba, cba, cba, 'A', 'V', 'I', ' '}, avi},
 642     }, // 82 R
 643     {
 644         {sqlite3db, sqlite3},
 645     }, // 83 S
 646     nil, // 84 T
 647     nil, // 85 U
 648     nil, // 86 V
 649     nil, // 87 W
 650     nil, // 88 X
 651     nil, // 89 Y
 652     nil, // 90 Z
 653     nil, // 91 [
 654     nil, // 92 \
 655     nil, // 93 ]
 656     nil, // 94 ^
 657     nil, // 95 _
 658     nil, // 96 `
 659     nil, // 97 a
 660     nil, // 98 b
 661     {
 662         {[]byte{'c', 'a', 'f', 'f', 000, 001, 000, 000}, caf},
 663     }, // 99 c
 664     nil, // 100 d
 665     nil, // 101 e
 666     {
 667         {[]byte{'f', 'L', 'a', 'C'}, flac},
 668     }, // 102 f
 669     nil, // 103 g
 670     nil, // 104 h
 671     nil, // 105 i
 672     nil, // 106 j
 673     nil, // 107 k
 674     nil, // 108 l
 675     nil, // 109 m
 676     nil, // 110 n
 677     nil, // 111 o
 678     nil, // 112 p
 679     nil, // 113 q
 680     nil, // 114 r
 681     nil, // 115 s
 682     nil, // 116 t
 683     nil, // 117 u
 684     nil, // 118 v
 685     nil, // 119 w
 686     nil, // 120 x
 687     nil, // 121 y
 688     nil, // 122 z
 689     {
 690         {[]byte{'{', '\\', 'r', 't', 'f'}, rtf},
 691     }, // 123 {
 692     nil, // 124 |
 693     nil, // 125 }
 694     nil, // 126
 695     {
 696         {[]byte{127, 'E', 'L', 'F'}, elf},
 697     }, // 127
 698     nil, // 128
 699     nil, // 129
 700     nil, // 130
 701     nil, // 131
 702     nil, // 132
 703     nil, // 133
 704     nil, // 134
 705     nil, // 135
 706     nil, // 136
 707     {
 708         {[]byte{0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A}, png},
 709     }, // 137
 710     nil, // 138
 711     nil, // 139
 712     nil, // 140
 713     nil, // 141
 714     nil, // 142
 715     nil, // 143
 716     nil, // 144
 717     nil, // 145
 718     nil, // 146
 719     nil, // 147
 720     nil, // 148
 721     nil, // 149
 722     nil, // 150
 723     nil, // 151
 724     nil, // 152
 725     nil, // 153
 726     nil, // 154
 727     nil, // 155
 728     nil, // 156
 729     nil, // 157
 730     nil, // 158
 731     nil, // 159
 732     nil, // 160
 733     nil, // 161
 734     nil, // 162
 735     nil, // 163
 736     nil, // 164
 737     nil, // 165
 738     nil, // 166
 739     nil, // 167
 740     nil, // 168
 741     nil, // 169
 742     nil, // 170
 743     nil, // 171
 744     nil, // 172
 745     nil, // 173
 746     nil, // 174
 747     nil, // 175
 748     nil, // 176
 749     nil, // 177
 750     nil, // 178
 751     nil, // 179
 752     nil, // 180
 753     nil, // 181
 754     nil, // 182
 755     nil, // 183
 756     nil, // 184
 757     nil, // 185
 758     nil, // 186
 759     nil, // 187
 760     nil, // 188
 761     nil, // 189
 762     nil, // 190
 763     nil, // 191
 764     nil, // 192
 765     nil, // 193
 766     nil, // 194
 767     nil, // 195
 768     nil, // 196
 769     nil, // 197
 770     nil, // 198
 771     nil, // 199
 772     nil, // 200
 773     nil, // 201
 774     nil, // 202
 775     nil, // 203
 776     nil, // 204
 777     nil, // 205
 778     nil, // 206
 779     nil, // 207
 780     nil, // 208
 781     nil, // 209
 782     nil, // 210
 783     nil, // 211
 784     nil, // 212
 785     nil, // 213
 786     nil, // 214
 787     nil, // 215
 788     nil, // 216
 789     nil, // 217
 790     nil, // 218
 791     nil, // 219
 792     nil, // 220
 793     nil, // 221
 794     nil, // 222
 795     nil, // 223
 796     nil, // 224
 797     nil, // 225
 798     nil, // 226
 799     nil, // 227
 800     nil, // 228
 801     nil, // 229
 802     nil, // 230
 803     nil, // 231
 804     nil, // 232
 805     nil, // 233
 806     nil, // 234
 807     nil, // 235
 808     nil, // 236
 809     nil, // 237
 810     nil, // 238
 811     nil, // 239
 812     nil, // 240
 813     nil, // 241
 814     nil, // 242
 815     nil, // 243
 816     nil, // 244
 817     nil, // 245
 818     nil, // 246
 819     nil, // 247
 820     nil, // 248
 821     nil, // 249
 822     nil, // 250
 823     nil, // 251
 824     nil, // 252
 825     nil, // 253
 826     nil, // 254
 827     {
 828         {[]byte{0xFF, 0xD8, 0xFF}, jpg},
 829         {[]byte{0xFF, 0xF3, 0x48, 0xC4, 0x00}, mp3},
 830         {[]byte{0xFF, 0xFB}, mp3},
 831     }, // 255
 832 }