zip.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #pragma once
  2. //creates uncompressed ZIP archives
  3. #include <nall/string.hpp>
  4. #include <nall/hash/crc32.hpp>
  5. namespace nall::Encode {
  6. struct ZIP {
  7. ZIP(const string& filename) {
  8. fp.open(filename, file::mode::write);
  9. timestamp = time(nullptr);
  10. }
  11. //append path: append("path/");
  12. //append file: append("path/file", data, size);
  13. auto append(string filename, const uint8_t* data = nullptr, uint size = 0u, time_t timestamp = 0) -> void {
  14. filename.transform("\\", "/");
  15. if(!timestamp) timestamp = this->timestamp;
  16. uint32_t checksum = Hash::CRC32({data, size}).digest().hex();
  17. directory.append({filename, timestamp, checksum, size, (uint32_t)fp.offset()});
  18. fp.writel(0x04034b50, 4); //signature
  19. fp.writel(0x0014, 2); //minimum version (2.0)
  20. fp.writel(0x0000, 2); //general purpose bit flags
  21. fp.writel(0x0000, 2); //compression method (0 = uncompressed)
  22. fp.writel(makeTime(timestamp), 2);
  23. fp.writel(makeDate(timestamp), 2);
  24. fp.writel(checksum, 4);
  25. fp.writel(size, 4); //compressed size
  26. fp.writel(size, 4); //uncompressed size
  27. fp.writel(filename.length(), 2); //file name length
  28. fp.writel(0x0000, 2); //extra field length
  29. fp.print(filename); //file name
  30. fp.write({data, size}); //file data
  31. }
  32. ~ZIP() {
  33. //central directory
  34. uint baseOffset = fp.offset();
  35. for(auto& entry : directory) {
  36. fp.writel(0x02014b50, 4); //signature
  37. fp.writel(0x0014, 2); //version made by (2.0)
  38. fp.writel(0x0014, 2); //version needed to extract (2.0)
  39. fp.writel(0x0000, 2); //general purpose bit flags
  40. fp.writel(0x0000, 2); //compression method (0 = uncompressed)
  41. fp.writel(makeTime(entry.timestamp), 2);
  42. fp.writel(makeDate(entry.timestamp), 2);
  43. fp.writel(entry.checksum, 4);
  44. fp.writel(entry.size, 4); //compressed size
  45. fp.writel(entry.size, 4); //uncompressed size
  46. fp.writel(entry.filename.length(), 2); //file name length
  47. fp.writel(0x0000, 2); //extra field length
  48. fp.writel(0x0000, 2); //file comment length
  49. fp.writel(0x0000, 2); //disk number start
  50. fp.writel(0x0000, 2); //internal file attributes
  51. fp.writel(0x00000000, 4); //external file attributes
  52. fp.writel(entry.offset, 4); //relative offset of file header
  53. fp.print(entry.filename);
  54. }
  55. uint finishOffset = fp.offset();
  56. //end of central directory
  57. fp.writel(0x06054b50, 4); //signature
  58. fp.writel(0x0000, 2); //number of this disk
  59. fp.writel(0x0000, 2); //disk where central directory starts
  60. fp.writel(directory.size(), 2); //number of central directory records on this disk
  61. fp.writel(directory.size(), 2); //total number of central directory records
  62. fp.writel(finishOffset - baseOffset, 4); //size of central directory
  63. fp.writel(baseOffset, 4); //offset of central directory
  64. fp.writel(0x0000, 2); //comment length
  65. fp.close();
  66. }
  67. protected:
  68. auto makeTime(time_t timestamp) -> uint16_t {
  69. tm* info = localtime(&timestamp);
  70. return (info->tm_hour << 11) | (info->tm_min << 5) | (info->tm_sec >> 1);
  71. }
  72. auto makeDate(time_t timestamp) -> uint16_t {
  73. tm* info = localtime(&timestamp);
  74. return ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday);
  75. }
  76. file_buffer fp;
  77. time_t timestamp;
  78. struct entry_t {
  79. string filename;
  80. time_t timestamp;
  81. uint32_t checksum;
  82. uint32_t size;
  83. uint32_t offset;
  84. };
  85. vector<entry_t> directory;
  86. };
  87. }