// SPDX-License-Identifier: MPL-2.0
// Some parts of the following driver is made using this guide: https://elm-chan.org/docs/fat_e.html
#include "vybuild.hpp"
#include "common/versions.h"
#include "api.h"
#include <dlfcn.h>
#include <map>
using namespace std;
typedef int (*crypto_sign_func_ptr)(uint8_t*,size_t*,const uint8_t*,size_t,const uint8_t*);
typedef void* (*sha3_512_func_ptr)(const void*,size_t,void*,int);
struct common_crypto_struct {
  crypto_sign_func_ptr crypto_sign;
  sha3_512_func_ptr sha3_512;
};
bool is_crypto_lib_available=false;
struct common_crypto_struct common_crypto;
uint64_t vftm_target_offset=0;
vector<uint8_t> vftm_target_content;
uint8_t manifest_keypair[CRYPTO_PUBLICKEYBYTES+CRYPTO_SECRETKEYBYTES]={0};
bool vybuild::fat32::is_crypto_lib_loaded() {
  return is_crypto_lib_available;
}
bool vybuild::fat32::is_vftm_target_set() {
  return vftm_target_offset!=0;
}
uint8_t* vybuild::fat32::get_manifest_keypair_buffer() {
  return manifest_keypair;
}
void vybuild::fat32::load_crypto_lib(string path) {
  void *handle;
  handle=dlopen(path.c_str(),RTLD_LAZY | RTLD_LOCAL);
  if (!handle) {
    throw std::runtime_error("Something went wrong during the loading of libcommoncrypto.so: "+string(dlerror()));
  }
  struct common_crypto_struct crypto;
  crypto.crypto_sign=reinterpret_cast<crypto_sign_func_ptr>(dlsym(handle,"crypto_sign_signature"));
  crypto.sha3_512=reinterpret_cast<sha3_512_func_ptr>(dlsym(handle,"sha3"));
  char *error=dlerror();
  if (error!=NULL) {
    dlclose(handle);
    throw std::runtime_error("Something went wrong during the loading of symbols from libcommoncrypto.so: "+string(error));
  }
  common_crypto=crypto;
  is_crypto_lib_available=true;
  return;
}
#pragma pack(1)
struct vystem_fat_trusted_manifest {
  uint8_t sig[8]={'V','y','F','a','t','S','i','g'};
  uint16_t manifest_version=VY_COMMON_VERSIONS_MANIFEST;
  uint16_t bootloader_version=VY_COMMON_VERSIONS_BOOTLOADER;
  uint32_t os_version=VY_COMMON_VERSIONS_OS;
  uint8_t fat_hash[64];
  uint8_t fat_build_id[32];
  uint64_t clusters_count;
  uint64_t bytes_per_sector;
  uint64_t sectors_per_cluster;
  uint64_t fat_size_bytes;
  uint64_t fat_offset_bytes;
  uint64_t bootloader_cluster_count;
  uint64_t bootloader_cluster_offset;
  uint64_t bootloader_build_id;
  uint8_t fat_header_hash[64];
  uint8_t bootloader_binary_hash[64];
  uint64_t signature_offset_bytes;
  uint64_t signature_size;
  uint8_t gpt_disk_guid[16];
  uint8_t gpt_efi_part_unique_guid[16];
  uint8_t manifest_hash[64];
};
#pragma pack()
#pragma pack(1)
struct fat32_header {
  uint8_t BS_JmpBoot[3]={0xeb,0x58,0x90};
  uint8_t BS_OEMName[8]={'V','Y','B','U','I','L','D','1'};
  uint16_t BPB_BytsPerSec=512;
  uint8_t BPB_SecPerClus=1;
  uint16_t BPB_RsvdSecCnt=32; // equivalent to 32 clusters
  uint8_t BPB_NumFATs=2;
  uint16_t BPB_RootEntCnt=0; // always 0 for FAT32
  uint16_t BPB_TotSec16=0;
  uint8_t BPB_Media=0xF8;
  uint16_t BPB_FATSz16=0;
  uint16_t BPB_SecPerTrk=0; // set to 0 because guide didn't indicate anything
  uint16_t BPB_NumHeads=0; // also set to 0 for same reason, useless on modern hardware
  uint32_t BPB_HiddSec=0; // i honestly don't know what value the UEFI firmware expect here, but i pray for it to work
  uint32_t BPB_TotSec32; // finally a useful field, amount of SECTORS in the whole fs
  uint32_t BPB_FATSz32; // amount of SECTORS in each FAT
  uint16_t BPB_ExtFlags=0; // too complicated for now
  uint16_t BPB_FSVer=0; // apparentaly not only FAT32 is very old and will never be updated, but current version is 0.0
  uint32_t BPB_RootClus=2; // root directory cluster
  uint16_t BPB_FSInfo=1; // this is an offset
  uint16_t BPB_BkBootSec=6; // this is also an offset
  uint8_t BPB_Reserved[12]={0}; // i wonder why so much useles space waster into reserved fields
  uint8_t BS_DrvNum=0x80; // hope this will work on UEFI firmware, i hate retrocompatibility, it lead to useles thing
  uint8_t BS_Reserved=0; // another useles field
  uint8_t BS_BootSig=0x29; // another signature
  uint32_t BS_VolID=0; // the guide want me to put a date and time here, but too bad, i'm lazy
  uint8_t BS_VolLab[11]={'V','Y','A','U','T','O','F','A','T','3','2'}; // yay, my custom volume label fit!
  uint8_t BS_FilSysType[8]={'F','A','T','3','2',' ',' ',' '}; // ok why not
  uint8_t BS_BootCode32[420]={0}; // who the fuck think this is a good idea to put a field so large into a fs header ?!
  uint16_t BS_Sign=0xAA55; // another signature
}; // yeah, exactly 512 bytes
#pragma pack()
#define ENTRY_FLAG_RO 0x01
#define ENTRY_FLAG_HIDDEN 0x02
#define ENTRY_FLAG_SYSTEM 0x04
#define ENTRY_FLAG_VOL_ID 0x08
#define ENTRY_FLAG_IS_DIR 0x10
#define ENTRY_FLAG_ARCHIVE 0x20
#define ENTRY_FLAG_LFN 0x0F
#pragma pack(1)
struct dir_entry {
  char name[11];
  uint8_t attributes;
  uint8_t sfn_flags=0;
  uint8_t creation_time_tenth=0;
  uint16_t creation_time=0;
  uint16_t creation_date=0;
  uint16_t last_access_date=0;
  uint16_t upper_part_first_cluster;
  uint16_t last_time_change=0;
  uint16_t last_data_change=0;
  uint16_t lower_part_first_cluster=0;
  uint32_t file_size=0;
};
#pragma pack()
#pragma pack(1)
struct fat32_fsinfo {
  uint32_t FSI_LeadSig=0x41615252;
  uint8_t FSI_Reserved1[480]={0}; // what the actual fuck is this... i think we have a new record
  uint32_t FSI_StrucSig=0x61417272;
  uint32_t FSI_Free_Count=0xFFFFFFFF;
  uint32_t FSI_Nxt_Free=0xFFFFFFFF;
  uint8_t FSI_Reserved2[12]={0};
  uint32_t FSI_TrailSig=0xAA550000;
};
#pragma pack()
struct fat32_container {
  fat32_header header;
  uint64_t fat_start_sector=header.BPB_RsvdSecCnt;
  uint64_t fat_total_sectors; // complete it with header.BPB_FATSz32 * header.BPB_NumFATs, amount of sectors in all FATs
  uint64_t data_start_sector; // complete with header.BPB_RsvdSecCnt + fat_total_sectors
  uint64_t data_total_sectors; // complete with header.BPB_TotSec32 - data_start_sector
  uint64_t clusters_count; // complete with data_total_sectors / header.BPB_SecPerClus
  uint64_t root_clusters_sector;
  vector<uint8_t> fat;
  vector<uint8_t> data;
  fat32_fsinfo fsinfo;
};
map<string,fat32_container> fs_map;
vector<uint8_t> get_urandom(size_t n) {
  vector<uint8_t> out(n);
  ifstream urandom("/dev/urandom",ios::binary);
  if (!urandom.is_open()) {
    throw runtime_error("Couldn't open /dev/urandom.");
  }
  urandom.read(reinterpret_cast<char*>(out.data()),n);
  if (urandom.gcount()!=static_cast<streamsize>(n)) {
    throw runtime_error("Coudln't open /dev/urandom.");
  }
  return out;
}
void read_sector(uint8_t *buf_start_ptr,size_t sector_index,uint8_t* out_buf) {
  size_t byte_offset=sector_index*512;
  memcpy(out_buf,buf_start_ptr+byte_offset,512);
  return;
}
void write_sector(uint8_t *buf_start_ptr,size_t sector_index,uint8_t* in_buf) {
  size_t byte_offset=sector_index*512;
  memcpy(buf_start_ptr+byte_offset,in_buf,512);
  return;
}
uint32_t fat_read_entry(fat32_container& cont,uint32_t cluster) {
  if (cluster>=cont.clusters_count+2) throw std::runtime_error("FAT cluster out of bounds");
  size_t offset=cluster*4;
  uint32_t value;
  memcpy(&value,cont.fat.data()+offset,4);
  return value & 0x0FFFFFFF;
}
void fat_write_entry(fat32_container& cont,uint32_t cluster,uint32_t value) {
  if (cluster>=cont.clusters_count+2) throw std::runtime_error("FAT cluster out of bounds");
  size_t offset=cluster*4;
  value&=0x0FFFFFFF;
  memcpy(cont.fat.data()+offset,&value,4);
  return;
}
vystem_fat_trusted_manifest generate_manifest(fat32_container& cont,string disk_guid_var,string efi_part_unique_guid_var) {
  if (!is_crypto_lib_available) throw std::runtime_error("Crypto library isn't available.");
  if (vftm_target_offset==0) throw std::runtime_error("VFTM target isn't set.");
  int zero_bytes=0;
  for (int i=0;i<sizeof(manifest_keypair);i++) {
    if (manifest_keypair[i]==0) {
      zero_bytes++;
    } else {
      break;
    }
  }
  if (zero_bytes==sizeof(manifest_keypair)) throw std::runtime_error("VFTM keypair isn't set.");
  vystem_fat_trusted_manifest vftm;
  uint32_t *f1=(uint32_t*)cont.fat.data();
  f1[0]=f1[1]=0;
  common_crypto.sha3_512(cont.fat.data(),cont.fat.size(),vftm.fat_hash,sizeof(vftm.fat_hash));
  fat_write_entry(cont,0,0x0FFFFFF8);
  fat_write_entry(cont,1,0xFFFFFFFF);
  auto fat_build_id=get_urandom(sizeof(vftm.fat_build_id));
  memcpy(vftm.fat_build_id,fat_build_id.data(),fat_build_id.size());
  vftm.clusters_count=cont.clusters_count;
  vftm.bytes_per_sector=cont.header.BPB_BytsPerSec;
  vftm.sectors_per_cluster=cont.header.BPB_SecPerClus;
  vftm.fat_size_bytes=cont.fat.size();
  vftm.fat_offset_bytes=cont.header.BPB_RsvdSecCnt*cont.header.BPB_BytsPerSec;
  vftm.bootloader_cluster_count=vftm_target_content.size()/(cont.header.BPB_BytsPerSec*cont.header.BPB_SecPerClus);
  vftm.bootloader_cluster_offset=vftm_target_offset;
  auto bootloader_build_id=get_urandom(8);
  memcpy(&vftm.bootloader_build_id,&bootloader_build_id,8);
  common_crypto.sha3_512(&cont.header,sizeof(cont.header),vftm.fat_header_hash,sizeof(vftm.fat_header_hash));
  common_crypto.sha3_512(vftm_target_content.data(),vftm_target_content.size(),vftm.bootloader_binary_hash,sizeof(vftm.bootloader_binary_hash));
  vftm.signature_offset_bytes=cont.header.BPB_TotSec32*cont.header.BPB_BytsPerSec;
  vftm.signature_size=CRYPTO_BYTES;
  auto disk_guid=vybuild::vars::get_global_variable("\\$"+disk_guid_var+"\\$");
  auto disk_guid_bytes=vybuild::disk::uuid4_to_bytes(disk_guid);
  memcpy(vftm.gpt_disk_guid,disk_guid_bytes.data(),disk_guid_bytes.size());
  auto efi_part_unique_guid=vybuild::vars::get_global_variable("\\$"+efi_part_unique_guid_var+"\\$");
  auto efi_part_unique_guid_bytes=vybuild::disk::uuid4_to_bytes(efi_part_unique_guid);
  memcpy(vftm.gpt_efi_part_unique_guid,efi_part_unique_guid_bytes.data(),efi_part_unique_guid_bytes.size());
  common_crypto.sha3_512(&vftm,sizeof(vftm)-64,vftm.manifest_hash,sizeof(vftm.manifest_hash));
  return vftm;
}
uint8_t* cluster_ptr(fat32_container& cont,uint32_t cluster) {
  size_t sector=(cluster-2)*cont.header.BPB_SecPerClus;
  return (uint8_t*)cont.data.data()+sector*512;
}
uint32_t fat_allocate_cluster(fat32_container& cont) {
  for (uint32_t i=2;i<cont.clusters_count+2;i++) {
    if (fat_read_entry(cont,i)==0) {
      fat_write_entry(cont,i,0x0FFFFFFF);
      memset(cluster_ptr(cont,i),0,cont.header.BPB_SecPerClus*512);
      return i;
    }
  }
  throw std::runtime_error("Fat32 image full.");
}
bool dir_add_entry(fat32_container& cont,uint32_t dir_cluster,dir_entry& entry) {
  if (dir_cluster<2 || dir_cluster>=cont.clusters_count+2) throw std::runtime_error("Directory cluster out of bounds.");
  dir_entry* entries=reinterpret_cast<dir_entry*>(cluster_ptr(cont,dir_cluster));
  size_t max_entries=(cont.header.BPB_SecPerClus*512)/sizeof(dir_entry);
  for (size_t i=0;i<max_entries;i++) {
    if (entries[i].name[0]==0) {
      entries[i]=entry;
      return true;
    }
  }
  return false;
}
void vybuild::fat32::create_fat32(string key,size_t size) {
  if (size%512!=0) throw std::runtime_error("Fat32 image size isn't a multiple of 512 bytes.");
  fat32_container cont;
  cont.header.BPB_TotSec32=size/512;
  uint32_t fat_sectors=1;
  while (true) {
    uint64_t data_sectors=cont.header.BPB_TotSec32-cont.header.BPB_RsvdSecCnt-fat_sectors*cont.header.BPB_NumFATs;
    uint64_t clusters=data_sectors/cont.header.BPB_SecPerClus;
    uint32_t new_fat_sectors=((clusters+2)*4+511)/512;
    if (new_fat_sectors==fat_sectors) break;
    fat_sectors=new_fat_sectors;
  }
  cont.header.BPB_FATSz32=fat_sectors;
  cont.fat_total_sectors=cont.header.BPB_FATSz32*cont.header.BPB_NumFATs;
  cont.data_start_sector=cont.header.BPB_RsvdSecCnt+cont.fat_total_sectors;
  cont.data_total_sectors=cont.header.BPB_TotSec32-cont.data_start_sector;
  cont.clusters_count=cont.data_total_sectors/cont.header.BPB_SecPerClus;
  cont.root_clusters_sector=cont.data_start_sector+(cont.header.BPB_RootClus-2)*cont.header.BPB_SecPerClus;
  cont.fat.resize(cont.header.BPB_FATSz32*512);
  memset(cont.fat.data(),0,cont.fat.size());
  fat_write_entry(cont,0,0x0FFFFFF8);
  fat_write_entry(cont,1,0xFFFFFFFF);
  fat_write_entry(cont,2,0x0FFFFFFF);
  cont.data.resize(cont.data_total_sectors*512);
  memset(cont.data.data(),0,cont.data.size());
  memset(cluster_ptr(cont,2),0,cont.header.BPB_SecPerClus*512);
  dir_entry label{};
  memset(label.name,' ',11);
  memcpy(label.name,"VYAUTOFAT32",11);
  label.attributes=ENTRY_FLAG_VOL_ID;
  label.lower_part_first_cluster=0;
  label.upper_part_first_cluster=0;
  label.file_size=0;
  dir_add_entry(cont,2,label);
  fs_map[key]=cont;
  return;
}
uint32_t path_find_dir_cluster(fat32_container& cont,string& path) {
  if (path.empty() || path[0]!='/') throw std::runtime_error("Path must be absolute.");
  if (path=="/") return 2;
  uint32_t current_cluster=2;
  size_t pos=1;
  while (pos<path.size()) {
    size_t next=path.find('/',pos);
    string part=(next==string::npos)?path.substr(pos):path.substr(pos,next-pos);
    char fat_name[11];
    memset(fat_name,' ',11);
    memcpy(fat_name,part.c_str(),min<size_t>(part.size(),8));
    dir_entry* entries=reinterpret_cast<dir_entry*>(cluster_ptr(cont,current_cluster));
    size_t max_entries=(cont.header.BPB_SecPerClus*512)/sizeof(dir_entry);
    bool found=false;
    for (size_t i=0;i<max_entries;i++) {
      if (entries[i].name[0]==0) break;
      if (!(entries[i].attributes & ENTRY_FLAG_IS_DIR)) continue;
      if (memcmp(entries[i].name,fat_name,11)==0) {
        current_cluster=(uint32_t(entries[i].upper_part_first_cluster)<<16)|entries[i].lower_part_first_cluster;
        found=true;
        break;
      }
    }
    if (!found) return 0;
    if (next==string::npos) break;
    pos=next+1;
  }
  return current_cluster;
}
uint32_t create_dir_path(fat32_container& cont,string& parent_folder,string& dir_name) {
  uint32_t parent_cluster=path_find_dir_cluster(cont,parent_folder);
  if (parent_cluster==0) throw std::runtime_error("Parent folder not found: "+parent_folder);
  uint32_t new_cluster=fat_allocate_cluster(cont);
  dir_entry entry{};
  memset(entry.name,' ', 11);
  memcpy(entry.name,dir_name.c_str(),min<size_t>(dir_name.size(),8));
  memcpy(entry.name+8,"   ",3);
  entry.attributes=ENTRY_FLAG_IS_DIR;
  entry.lower_part_first_cluster=new_cluster&0xFFFF;
  entry.upper_part_first_cluster=new_cluster>>16;
  entry.file_size=0;
  if (!dir_add_entry(cont,parent_cluster,entry)) throw std::runtime_error("Parent directory full.");
  dir_entry dot{};
  memset(dot.name,' ',11);
  memcpy(dot.name,".          ",11);
  dot.attributes=ENTRY_FLAG_IS_DIR;
  dot.lower_part_first_cluster=new_cluster&0xFFFF;
  dot.upper_part_first_cluster=new_cluster>>16;
  dot.file_size=0;
  dir_entry dotdot{};
  memset(dotdot.name,' ',11);
  memcpy(dotdot.name,"..         ",11);
  dotdot.attributes=ENTRY_FLAG_IS_DIR;
  dotdot.lower_part_first_cluster=parent_cluster&0xFFFF;
  dotdot.upper_part_first_cluster=parent_cluster>>16;
  dotdot.file_size=0;
  dir_add_entry(cont,new_cluster,dot);
  dir_add_entry(cont,new_cluster,dotdot);
  return new_cluster;
}
uint32_t create_dir(fat32_container& cont,string& full_path) {
  if (full_path.empty() || full_path[0]!='/') throw std::runtime_error("Path must be absolute.");
  size_t last_slash=full_path.find_last_of('/');
  if (last_slash==string::npos) throw std::runtime_error("Invalid path.");
  string parent=(last_slash==0)?"/":full_path.substr(0,last_slash);
  string dir_name=full_path.substr(last_slash+1);
  if (dir_name.empty()) throw std::runtime_error("Directory name is empty.");
  return create_dir_path(cont, parent, dir_name);
}
void vybuild::fat32::add_directory_fat32(string key,string full_path) {
  if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
  fat32_container& fat=fs_map[key];
  create_dir(fat,full_path);
  return;
}
uint32_t fat_allocate_chain(fat32_container& cont,uint32_t count) {
  if (count==0) throw std::runtime_error("Cluster chain size cannot be zero.");
  uint32_t max_cluster=cont.clusters_count+2;
  for (uint32_t start=2;start+count<=max_cluster;start++) {
    bool free=true;
    for (uint32_t i=0;i<count;i++) {
      if (fat_read_entry(cont,start+i)!=0) {
        free=false;
        break;
      }
    }
    if (!free) continue;
    for (uint32_t i=0;i<count-1;i++) {
      fat_write_entry(cont,start+i,start+i+1);
    }
    fat_write_entry(cont,start+count-1,0x0FFFFFFF);
    size_t cluster_size=cont.header.BPB_SecPerClus*512;
    memset(cluster_ptr(cont,start),0,cluster_size*count);
    return start;
  }
  throw std::runtime_error("Not enough contiguous clusters.");
}
uint32_t create_file(fat32_container& cont,string full_path,uint8_t *buffer_in,size_t file_size,bool is_vftm_target) {
  if (file_size!=0 && buffer_in==nullptr) throw std::runtime_error("Input buffer is null.");
  if (full_path.empty() || full_path[0]!='/') throw std::runtime_error("Path must be absolute.");
  size_t last_slash=full_path.find_last_of('/');
  if (last_slash==string::npos) throw std::runtime_error("Invalid path.");
  string parent=(last_slash==0)?"/":full_path.substr(0,last_slash);
  string file_name=full_path.substr(last_slash+1);
  if (file_name.empty()) throw std::runtime_error("File name is empty.");
  if (file_name.size()>12) throw std::runtime_error("File name including extension is too long.");
  size_t dot=file_name.find_last_of('.');
  if (dot==string::npos || dot==0) throw std::runtime_error("Invalid file name.");
  string name=file_name.substr(0,dot);
  string ext=file_name.substr(dot+1);
  if (ext.size()>3 || ext.size()==0) throw std::runtime_error("Invalid extension.");
  if (name.size()>8 || ext.size()==0) throw std::runtime_error("Invalid file name.");
  transform(name.begin(),name.end(),name.begin(),::toupper);
  transform(ext.begin(),ext.end(),ext.begin(),::toupper);
  uint32_t parent_cluster=path_find_dir_cluster(cont,parent);
  if (parent_cluster==0) throw std::runtime_error("Parent folder doesn't exist.");
  uint32_t cluster_size=cont.header.BPB_SecPerClus*512;
  uint32_t cluster_count=max<uint32_t>(1,(file_size+cluster_size-1)/cluster_size);
  uint32_t start_cluster=fat_allocate_chain(cont,cluster_count);
  dir_entry entry{};
  memset(entry.name,' ',11);
  memcpy(entry.name,name.c_str(),min<size_t>(name.size(),8));
  memcpy(entry.name+8,ext.data(),min<size_t>(ext.size(),3));
  entry.attributes=ENTRY_FLAG_ARCHIVE;
  entry.lower_part_first_cluster=start_cluster&0xFFFF;
  entry.upper_part_first_cluster=start_cluster>>16;
  entry.file_size=file_size;
  if (!dir_add_entry(cont,parent_cluster,entry)) throw std::runtime_error("Parent directory full.");
  memcpy(cluster_ptr(cont,start_cluster),buffer_in,file_size);
  if (is_vftm_target) {
    vftm_target_content.resize(cluster_count*cluster_size,0);
    memcpy(vftm_target_content.data(),buffer_in,file_size);
    vftm_target_offset=start_cluster;
  }
  return start_cluster;
}
void vybuild::fat32::add_file_fat32(string key,string full_path,uint8_t *buffer_in,size_t file_size,bool is_vftm_target) {
  if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
  fat32_container& fat=fs_map[key];
  create_file(fat,full_path,buffer_in,file_size,is_vftm_target);
  return;
}
void generate_vftm_sectors(const uint8_t* part_guid,int* s1,int* s2) {
  uint32_t h=0x811c9dc5;
  for (int i=0;i<16;i++) h=(h^part_guid[i])*0x01000193;
  *s1=8+(h%24);
  *s2=8+(((h>>16)%23+(*s1-7))%24);
}
void secure_erase(void *address,size_t size) {
  explicit_bzero(address,size);
}
vector<uint8_t> export_fat32(fat32_container& cont,bool generate_vftm,string disk_guid_var,string efi_part_unique_guid_var) {
  uint32_t total_sectors=cont.header.BPB_TotSec32;
  vector<uint8_t> img(total_sectors*512,0);
  auto write_sector=[&](uint32_t sector,const void* data,size_t size) {
    memcpy(img.data()+sector*512,data,size);
  };
  write_sector(0,&cont.header,sizeof(cont.header));
  write_sector(cont.header.BPB_BkBootSec,&cont.header,sizeof(cont.header));
  write_sector(1,&cont.fsinfo,sizeof(cont.fsinfo));
  write_sector(cont.header.BPB_BkBootSec+1,&cont.fsinfo,sizeof(cont.fsinfo));
  uint32_t fat_size_bytes=cont.header.BPB_FATSz32*512;
  uint32_t fat_sector_start=cont.header.BPB_RsvdSecCnt;
  memcpy(img.data()+fat_sector_start*512,cont.fat.data(),fat_size_bytes);
  uint32_t fat2_sector_start=fat_sector_start+cont.header.BPB_FATSz32;
  memcpy(img.data()+fat2_sector_start*512,cont.fat.data(),fat_size_bytes);
  uint32_t data_sector_start=cont.data_start_sector;
  memcpy(img.data()+data_sector_start*512,cont.data.data(),cont.data.size());
  if (generate_vftm) {
    auto efi_part_unique_guid=vybuild::vars::get_global_variable("\\$"+efi_part_unique_guid_var+"\\$");
    auto efi_part_unique_guid_bytes=vybuild::disk::uuid4_to_bytes(efi_part_unique_guid);
    auto vftm=generate_manifest(cont,disk_guid_var,efi_part_unique_guid_var);
    int manifest_sector,pk_manifest_sector;
    generate_vftm_sectors((uint8_t*)efi_part_unique_guid_bytes.data(),&manifest_sector,&pk_manifest_sector);
    uint8_t signature_manifest[CRYPTO_BYTES];
    size_t siglen;
    if (common_crypto.crypto_sign(signature_manifest,&siglen,(uint8_t*)&vftm,sizeof(vftm),manifest_keypair+CRYPTO_PUBLICKEYBYTES)) {
      secure_erase(manifest_keypair,sizeof(manifest_keypair));
      throw std::runtime_error("Couldn't sign VFTM.");
    }
    if (siglen!=CRYPTO_BYTES) {
      secure_erase(manifest_keypair,sizeof(manifest_keypair));
      throw std::runtime_error("The returned signature isn't the expected size.");
    }
    secure_erase(manifest_keypair+CRYPTO_PUBLICKEYBYTES,CRYPTO_SECRETKEYBYTES);
    memcpy(img.data()+manifest_sector*cont.header.BPB_BytsPerSec,&vftm,sizeof(vftm));
    memcpy(img.data()+pk_manifest_sector*cont.header.BPB_BytsPerSec,manifest_keypair,CRYPTO_PUBLICKEYBYTES);
    img.resize(img.size()+CRYPTO_BYTES);
    memcpy(img.data()+vftm.signature_offset_bytes,signature_manifest,CRYPTO_BYTES);
  }
  return img;
}
vector<uint8_t> vybuild::fat32::export_fat32(string key,bool generate_vftm,string disk_guid_var,string efi_part_unique_guid_var) {
  if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
  fat32_container& fat=fs_map[key];
  return export_fat32(fat,generate_vftm,disk_guid_var,efi_part_unique_guid_var);
}
