使用libzip库完成i3s标准的slpk打包

59 阅读2分钟

项目中的生成slpk模块遇到问题,记录一下。初步判定是slpk内的文件组织方式有误,没有按照

i3s和slpk是什么

i3s(indexed 3D scene layer)存储3d场景数据的一个标准,slpk是这个标准的一个具体实现,在1.6的版本支持四种数据类型,项目中用到的是integrated mesh,跟其他几个的区别在哪,我目前也不太懂(),在他们的github中有详细描述,如果要自己实现slpk的话,建议去看ogc上的规范细则(slpkV1.6),个人感觉比GitHub写得详细。

libzip

源自c时代的一个用于压缩文件的库,官网手册写得一塌糊涂,在外网找到一些别人写的例子才慢慢摸明白怎么用。。。libzip example to create archive · GitHub

#include <iostream>
#include <string>

#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

#include <zip.h>

static bool is_dir(const std::string& dir)
{
  struct stat st;
  ::stat(dir.c_str(), &st);
  return S_ISDIR(st.st_mode);
}

static void walk_directory(const std::string& startdir, const std::string& inputdir, zip_t *zipper)
{
  DIR *dp = ::opendir(inputdir.c_str());
  if (dp == nullptr) {
    throw std::runtime_error("Failed to open input directory: " + std::string(::strerror(errno)));
  }

  struct dirent *dirp;
  while ((dirp = readdir(dp)) != NULL) {
    if (dirp->d_name != std::string(".") && dirp->d_name != std::string("..")) {
      std::string fullname = inputdir + "/" + dirp->d_name;
      if (is_dir(fullname)) {
        if (zip_dir_add(zipper, fullname.substr(startdir.length() + 1).c_str(), ZIP_FL_ENC_UTF_8) < 0) {
          throw std::runtime_error("Failed to add directory to zip: " + std::string(zip_strerror(zipper)));
        }
        walk_directory(startdir, fullname, zipper);
      } else {
        zip_source_t *source = zip_source_file(zipper, fullname.c_str(), 0, 0);
        if (source == nullptr) {
          throw std::runtime_error("Failed to add file to zip: " + std::string(zip_strerror(zipper)));
        }
        if (zip_file_add(zipper, fullname.substr(startdir.length() + 1).c_str(), source, ZIP_FL_ENC_UTF_8) < 0) {
          zip_source_free(source);
          throw std::runtime_error("Failed to add file to zip: " + std::string(zip_strerror(zipper)));
        }
      }
    }
  }
  ::closedir(dp);
}

static void zip_directory(const std::string& inputdir, const std::string& output_filename)
{
  int errorp;
  zip_t *zipper = zip_open(output_filename.c_str(), ZIP_CREATE | ZIP_EXCL, &errorp);
  if (zipper == nullptr) {
    zip_error_t ziperror;
    zip_error_init_with_code(&ziperror, errorp);
    throw std::runtime_error("Failed to open output file " + output_filename + ": " + zip_error_strerror(&ziperror));
  }

  try {
    walk_directory(inputdir, inputdir, zipper);
  } catch(...) {
    zip_close(zipper);
    throw;
  }

  zip_close(zipper);
}

int main(int argc, char *argv[])
{
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <input_dir> <output_file>\n", argv[0]);
    return 1;
  }
  zip_directory(argv[1], argv[2]);

  return 0;
}

参照slpk的格式、压缩要求,很快就能写出来了(虽然我搞了挺久的)