Trivial Solutions
                    The LoseThos 64-bit PC Operating System
File:/LT/Adam/DskCDDVD.APZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.

#help_index "File/CD DVD"

U0 FillU16Palindrome(U16Palindrome *dst,U16 w)
{
  dst->big=BigEndianU16(w);
  dst->little=w;
}

U0 FillU32Palindrome(U32Palindrome *dst,U64 d)
{
  dst->big=BigEndianU32(d);
  dst->little=d;
}


class CCdUserData
{
  U64 loc,path_entry_num,
        short_dir_blks,long_dir_blks;
};

U64 CDFileCreateDo(LTFile *out_file,LTDirEntry *tempm,
                    ISO1DirEntry *tempi,ISO1DirEntry *tempi2,
                    U64 *_cur_blk,
                    LTDirEntry *parent,BoolI8 write)
{
  CCdUserData *tempc;
  LTDirEntry *tempm1,*tempm2;
  LTFile *in_file;
  U8 *buf=MAlloc(CD_BLK_SIZE),*ptr1,*ptr2;
  ISO1DirEntry *dir_blk_buf=CAlloc(CD_BLK_SIZE*128),
              *de=dir_blk_buf,*de1;
  ISO1DirEntry *dir_blk_buf2=CAlloc(CD_BLK_SIZE*128),
              *de2=dir_blk_buf2,*de12;
  U64 i,n;

  tempc=parent->user_data;

  de->len=sizeof(ISO1DirEntry)-1;
  de->ext_attr_len=0;
  FillU16Palindrome(&de->vol_seq_num,1);
  LTDate2ISO1(&de->date,tempm->datetime);
  de->flags=ISO1_ATTR_DIR;
  de->name_len=1;
  de->name=0;
  de->len+=de->name_len;
  de><(U8 *)+=de->len;

  de->len=sizeof(ISO1DirEntry)-1;
  de->ext_attr_len=0;
  FillU32Palindrome(&de->loc,tempc->loc);
  FillU32Palindrome(&de->size,tempc->short_dir_blks*CD_BLK_SIZE);
  FillU16Palindrome(&de->vol_seq_num,1);
  LTDate2ISO1(&de->date,parent->datetime);
  de->flags=ISO1_ATTR_DIR;
  de->name_len=1;
  de->name=1;
  de->len+=de->name_len;
  de><(U8 *)+=de->len;

  de2->len=sizeof(ISO1DirEntry)-1;
  de2->ext_attr_len=0;
  FillU16Palindrome(&de2->vol_seq_num,1);
  LTDate2ISO1(&de2->date,tempm->datetime);
  de2->flags=ISO1_ATTR_DIR;
  de2->name_len=1;
  de2->name=0;
  de2->len=de2->len+de2->name_len;
  de2><(U8 *)+=de2->len;

  de2->len=sizeof(ISO1DirEntry)-1;
  de2->ext_attr_len=0;
  FillU32Palindrome(&de2->loc,tempc->loc+tempc->short_dir_blks);
  FillU32Palindrome(&de2->size,tempc->long_dir_blks*CD_BLK_SIZE);
  FillU16Palindrome(&de2->vol_seq_num,1);
  LTDate2ISO1(&de2->date,parent->datetime);
  de2->flags=ISO1_ATTR_DIR;
  de2->name_len=1;
  de2->name=1;
  de2->len+=de2->name_len;
  de2><(U8 *)+=de2->len;

  tempm1=tempm->sub;
  while (tempm1) {
    tempm2=tempm1->next;
    if (!write) tempm1->user_data=CAlloc(sizeof(CCdUserData));
    de1=de;
    de12=de2;
    if (tempm1->attr & LT_ATTR_DIR) {
      n=CDFileCreateDo(out_file,tempm1,de,de2,_cur_blk,tempm,write);
      de><(U8 *)+=sizeof(ISO1DirEntry)-1+n;
      de2><(U8 *)+=sizeof(ISO1DirEntry)-1+n<<1;
    } else {
      tempc=tempm1->user_data;
      de->len=sizeof(ISO1DirEntry)-1;
      de->ext_attr_len=0;
      FillU32Palindrome(&de->loc,*_cur_blk);
      tempc->loc=*_cur_blk;
      if (write)
        PrintF("%X:%s\r\n",*_cur_blk,tempm1->full_name);
      FillU32Palindrome(&de->size,tempm1->size);
      FillU16Palindrome(&de->vol_seq_num,1);
      LTDate2ISO1(&de->date,tempm1->datetime);
      de->flags=0;
      de->name_len=StrLen(tempm1->name);
      StrCpy(&de->name,tempm1->name);
      de->len=de->len+de->name_len;
      de><(U8 *)+=de->len;

      de2->len=sizeof(ISO1DirEntry)-1;
      de2->ext_attr_len=0;
      FillU32Palindrome(&de2->loc,*_cur_blk);
      FillU32Palindrome(&de2->size,tempm1->size);
      FillU16Palindrome(&de2->vol_seq_num,1);
      LTDate2ISO1(&de2->date,tempm1->datetime);
      de2->flags=0;
      de2->name_len=StrLen(tempm1->name)<<1;
      ptr1=&de2->name;
      ptr2=&tempm1->name;
      for (i=0;i<de2->name_len;i=i+2) {
        ptr1++;
        *ptr1++=*ptr2++;
      }
      de2->len+=de2->name_len;
      de2><(U8 *)+=de2->len;

      in_file=FOpen(tempm1->full_name,"r");
      for (i=0;i<(FSize(in_file)+CD_BLK_SIZE-1)/CD_BLK_SIZE;i++) {
        n=4;
        if ((i+1)<<2>(FSize(in_file)+BLK_SIZE-1)>>BLK_SIZE_BITS) {
          n=(FSize(in_file)+BLK_SIZE-1)>>BLK_SIZE_BITS%4;
          MemSet(buf,0,CD_BLK_SIZE);
        }
        if (write) {
          FRBlks(in_file,buf,i<<2,n);
          FWBlks(out_file,buf,*_cur_blk<<2,n);
        }
        *_cur_blk+=1;
      }
      FClose(in_file);
    }
    if ((de1><(U8 *)-dir_blk_buf><(U8 *))/CD_BLK_SIZE!=
        (de><(U8 *) -dir_blk_buf><(U8 *))/CD_BLK_SIZE) {
      i=de1->len;
      MemCpy(buf,de1,i);
      MemSet(de1,0,i);
      de=dir_blk_buf><(U8 *)+(de><(U8 *)-dir_blk_buf><(U8 *))/CD_BLK_SIZE*CD_BLK_SIZE;
      MemCpy(de,buf,i);
      de><(U8 *)+=i;
    }
    if ((de12><(U8 *)-dir_blk_buf2><(U8 *))/CD_BLK_SIZE!=
        (de2><(U8 *) -dir_blk_buf2><(U8 *))/CD_BLK_SIZE) {
      i=de12->len;
      MemCpy(buf,de12,i);
      MemSet(de12,0,i);
      de2><(U8 *)=dir_blk_buf2><(U8 *)+(de2><(U8 *)-dir_blk_buf2><(U8 *))/CD_BLK_SIZE*CD_BLK_SIZE;
      MemCpy(de2,buf,i);
      de2><(U8 *)+=i;
    }
    tempm1=tempm2;
  }

  tempc=tempm->user_data;

  tempi->len=sizeof(ISO1DirEntry)-1;
  tempi->ext_attr_len=0;
  tempi->flags=ISO1_ATTR_DIR;
  if (!tempm->name[0]) {
    tempi->name_len=1;
    tempi->name=1;
  } else {
    tempi->name_len=StrLen(tempm->name);
    StrCpy(&tempi->name,tempm->name);
  }
  tempi->len+=tempi->name_len;

  n=de><(U8 *)+1-dir_blk_buf><(U8 *);
  n=(n+CD_BLK_SIZE-1)/CD_BLK_SIZE;
  FillU32Palindrome(&tempi->size,n*CD_BLK_SIZE);
  FillU32Palindrome(&tempi->loc,*_cur_blk);
  tempc->short_dir_blks=n;
  tempc->loc=*_cur_blk;
  FillU32Palindrome(&dir_blk_buf->size,n*CD_BLK_SIZE);
  FillU32Palindrome(&dir_blk_buf->loc,*_cur_blk);
  FillU16Palindrome(&tempi->vol_seq_num,1);
  LTDate2ISO1(&tempi->date,tempm->datetime);
  if (write)
    PrintF("%X:%s\r\n",*_cur_blk,tempm->full_name);
  if (write)
    FWBlks(out_file,dir_blk_buf,*_cur_blk<<2,n<<2);
  *_cur_blk+=n;

  tempi2->len=sizeof(ISO1DirEntry)-1;
  tempi2->ext_attr_len=0;
  tempi2->flags=ISO1_ATTR_DIR;
  if (!tempm->name[0]) {
    tempi2->name_len=1;
    tempi->name=1;
  } else {
    tempi2->name_len=StrLen(tempm->name)<<1;
    ptr1=&tempi2->name;
    ptr2=&tempm->name;
    for (i=0;i<tempi2->name_len;i=i+2) {
      ptr1++;
      *ptr1++=*ptr2++;
    }
  }
  tempi2->len+=tempi2->name_len;
  n=de2><(U8 *)+1-dir_blk_buf2><(U8 *);
  n=(n+CD_BLK_SIZE-1)/CD_BLK_SIZE;
  FillU32Palindrome(&tempi2->size,n*CD_BLK_SIZE);
  FillU32Palindrome(&tempi2->loc,*_cur_blk);
  tempc->long_dir_blks=n;
  FillU32Palindrome(&dir_blk_buf2->size,n*CD_BLK_SIZE);
  FillU32Palindrome(&dir_blk_buf2->loc,*_cur_blk);
  FillU16Palindrome(&tempi2->vol_seq_num,1);
  LTDate2ISO1(&tempi2->date,tempm->datetime);
  if (write)
    PrintF("%X:%s\r\n",*_cur_blk,tempm->full_name);
  if (write)
    FWBlks(out_file,dir_blk_buf2,*_cur_blk<<2,n<<2);
  *_cur_blk+=n;

  Free(dir_blk_buf);
  Free(dir_blk_buf2);
  Free(buf);
  return tempi->name_len;
}

U64 CDTableLen(LTDirEntry *tempm,U64 *size1,U64 *size2,U64 cur_depth)
//returns depth
{
  LTDirEntry *tempm1=tempm->sub;
  U64 max_depth=cur_depth,i;
  while (tempm1) {
    if (tempm1->attr & LT_ATTR_DIR) {
      *size1+=sizeof(ISO1PathTableEntry)-2+ (StrLen(tempm1->name)+1)&-0x2;
      *size2+=sizeof(ISO1PathTableEntry)-2+StrLen(tempm1->name)<<1;
      i=CDTableLen(tempm1,size1,size2,cur_depth+1);
      if (i>max_depth) max_depth=i;
    }
    tempm1=tempm1->next;
  }
  return max_depth;
}

U0 CDFillPathTable(LTDirEntry *tempm,
  ISO1PathTableEntry **itabbuf,ISO1PathTableEntry **itabbuf2,
  U64 parent_entry_num,BoolI8 big_endian,U64 *first_free,I64 cur_level,I64 output_level)
{
  U8 *ptr1,*ptr2;
  U64 i;
  ISO1PathTableEntry *tabbuf=*itabbuf;
  ISO1PathTableEntry *tabbuf2=*itabbuf2;
  LTDirEntry *tempm1=tempm->sub,*tempm2;
  CCdUserData *tempc;
 
  if (cur_level==output_level) {
    while (tempm1) {
      if (tempm1->attr & LT_ATTR_DIR) {
        tempc=tempm1->user_data;
        tempc->path_entry_num=*first_free;
        tabbuf->name_len=StrLen(tempm1->name);
        if (big_endian) {
          tabbuf->blk=BigEndianU32(tempc->loc);
          tabbuf->parent_entry_num=BigEndianU16(parent_entry_num);
        } else {
          tabbuf->blk=tempc->loc;
          tabbuf->parent_entry_num=parent_entry_num;
        }
        StrCpy(&tabbuf->name,tempm1->name);

        tabbuf><(U8 *)+=sizeof(ISO1PathTableEntry)-2+
          (StrLen(tempm1->name)+1) & -0x2;

        tabbuf2->name_len=StrLen(tempm1->name)<<1;
        if (big_endian) {
          tabbuf2->blk=BigEndianU32(tempc->loc+tempc->short_dir_blks);
          tabbuf2->parent_entry_num=BigEndianU16(parent_entry_num);
        } else {
          tabbuf2->blk=tempc->loc+tempc->short_dir_blks;
          tabbuf2->parent_entry_num=parent_entry_num;
        }
        ptr1=&tabbuf2->name;
        ptr2=&tempm1->name;
        for (i=0;i<tabbuf2->name_len;i=i+2) {
          ptr1++;
          *ptr1++=*ptr2++;
        }
        tabbuf2><(U8 *)+=sizeof(ISO1PathTableEntry)-2+
          StrLen(tempm1->name)<<1;
        *first_free+=1;
      }
      tempm1=tempm1->next;
    }
    *itabbuf=tabbuf;
    *itabbuf2=tabbuf2;
  }
  tempm1=tempm->sub;
  while (tempm1) {
    tempm2=tempm1->next;
    if (tempm1->attr & LT_ATTR_DIR) {
      tempc=tempm1->user_data;
      CDFillPathTable(tempm1,itabbuf,itabbuf2,tempc->path_entry_num,
         big_endian,first_free,cur_level+1,output_level);
    }
    tempm1=tempm2;
  }
}

class ElTorito
{
  U16 w[16];
  U8 bootable; //88=bootable 00=not bootable
  U8 media_type; //0=no emulation 4=hard disk
  U16 load_seg; //0000->07C0
  U8 sys_type;
  U8 zero;
  U16 sector_cnt;
  U32 load_rba; //start address of virtual disk
  U8 zero2[20];
};



/****
I have the following code in my start-up script.

'E' is my primary hard drive LoseThos partition

'T' is my CD/DVD drive

'Z' is a RAM temporary drive from which I make an ISO image.  You could use a file partition instead of RAM.

#define PROD_CDROM_CONFIG       "b10x20000\rt3 \r\r\r\r\r"

U0 BurnProductionCDImage()
{
  Cd("E:/LT");

  //This enters my config
  //for compiling OSMain.
  Auto(PROD_CDROM_CONFIG);

  InstallBoot('E','T');
  Fmt('Z',TRUE,FALSE);
  Drv('Z');
  DelTree("/LT");
  CopyTree("E:/LT","/LT");
  DelTree("/Temp");
  MkDir("/Temp");
  DelTree("/0000Boot");
  CopyTree("E:/0000Boot",
    "/0000Boot");
  CDFileCreate(sys_dft_iso_name,
    'Z',CD_BOOT);
  CDImageWrite('T',
    sys_dft_iso_name);
}

Study my account examples My Technique and
the recovery CD maker in  Recovery Disk Maker.
****/

public U64 CDFileCreate(U8 *filename,U8 src_drv,
                          U8 *bootfile=NULL,I64 emulation=0)
//See the above sample code
//0=no emu 2=1.44meg 4=hard drive
{
  ISO1PriDesc *iso =CAlloc(CD_BLK_SIZE),
                       *iso1=CAlloc(CD_BLK_SIZE),
                       *iso2=CAlloc(CD_BLK_SIZE),
                       *iso3=CAlloc(CD_BLK_SIZE);
  LTDirEntry *rootdir=CAlloc(sizeof(LTDirEntry));
  U64 cur_blk=0,bootfile_cd_blks=0,tabsize,tabsize2,first_free,max_depth;
  U32 *d;
  U8 files_find_mask[8];
  U8 *filename2;
  ElTorito *et=CAlloc(CD_BLK_SIZE);
  U8 *zero_buf=CAlloc(CD_BLK_SIZE),
     *bootfile_buf=NULL;
  ISO1PathTableEntry *tabbuf=NULL,*tabbuf2=NULL,
                    *itabbuf,*itabbuf2;
  BoolI8 okay=TRUE;
  U64 i,j;
  LTFile *out_file=NULL;
  ISO1DirEntry *tempi;
  CCdUserData *tempc;

  if (!filename)
    filename=sys_dft_iso_name;
  filename2=DftExt(filename,"ISO");

  SPrintF(files_find_mask,"%c:/*",src_drv);
  if (bootfile) {
    bootfile_buf=FileRead(bootfile,&bootfile_cd_blks);
    if (bootfile_cd_blks>CD_BLK_SIZE*4) {
      PutS(ST_ERR_ST "Boot files limited to 0x2000 bytes\r\n");
      goto done1;
    }
    bootfile_cd_blks=(bootfile_cd_blks+CD_BLK_SIZE-1)/CD_BLK_SIZE;
  }
  rootdir->attr=LT_ATTR_DIR;
  rootdir->sub=FilesFind(files_find_mask,1<<FUf_RECURSE);
  rootdir->datetime=GetCurTimeLTDate;
  rootdir->user_data=CAlloc(sizeof(CCdUserData));
  tempc=rootdir->user_data;
  tempc->path_entry_num=1;

  if (okay) {
    cur_blk=CD_FILE_OFFSET>>2;
    if (bootfile_buf)
      //preboot and bootloader
      cur_blk+=1+bootfile_cd_blks;
    CDFileCreateDo(out_file,rootdir,
        &iso->root_dir_record,
        &iso2->root_dir_record,
        &cur_blk,rootdir,FALSE);
    tabsize=sizeof(ISO1PathTableEntry);
    tabsize2=sizeof(ISO1PathTableEntry);
    max_depth=CDTableLen(rootdir,&tabsize,&tabsize2,1);
    FillU32Palindrome(&iso->path_table_size,tabsize);
    FillU32Palindrome(&iso2->path_table_size,tabsize2);
    tabsize=(tabsize+CD_BLK_SIZE-1)/CD_BLK_SIZE;
    cur_blk+=tabsize<<1;
    tabsize2=(tabsize2+CD_BLK_SIZE-1)/CD_BLK_SIZE;
    cur_blk+=tabsize2<<1;
  }
  if (okay) {
    if (FileAttr(filename2) & LT_ATTR_CONTIGUOUS)
      out_file=FOpen(filename2,"wc",cur_blk<<2);
    else
      out_file=FOpen(filename2,"w",cur_blk<<2);
    if (!out_file)
      okay=FALSE;
  }
  if (okay) {
    cur_blk=0;
    while (cur_blk<CD_FILE_OFFSET>>2) {
      FWBlks(out_file,zero_buf,cur_blk<<2,4);
      cur_blk++;
    }
    iso->type=ISO1T_PRI_VOL_DESC;
    StrCpy(iso->id,"CD001");
    iso->version=1;
    FillU16Palindrome(&iso->vol_set_size,1);
    FillU16Palindrome(&iso->vol_seq_num,1);
    FillU16Palindrome(&iso->log_block_size,CD_BLK_SIZE);
    iso->file_structure_version=1;

    iso2->type=ISO1T_SUPPLEMENTARY_DESC;
    StrCpy(iso2->id,"CD001");
    iso2->version=1;
    FillU16Palindrome(&iso2->vol_set_size,1);
    FillU16Palindrome(&iso2->vol_seq_num,1);
    FillU16Palindrome(&iso2->log_block_size,CD_BLK_SIZE);
    iso2->file_structure_version=1;

    iso1->type=ISO1T_BOOT_RECORD;
    StrCpy(iso1->id,"CD001");
    iso1->version=1;
    StrCpy(iso1><(U8 *)+7,"EL TORITO SPECIFICATION");

    cur_blk=CD_FILE_OFFSET>>2;

    if (bootfile_buf) {
      d=iso1><(U8 *)+0x47;
      *d=cur_blk;
      et->w[0]=1;
      StrCpy(&et->w[2],"LoseThos");
      et->w[15]=0xAA55;
      j=0;
      for (i=0;i<16;i++) //Checksum
        j+=et->w[i];
      et->w[14]=-j;
      if (bootfile_buf)
        et->bootable=0x88;
      //0=no emu 2=1.44meg 4=hard drive
      et->media_type=emulation;
      et->sector_cnt=4;  //5 seems like the limit, 4 is safer
      et->load_rba=cur_blk+1;
//CDBOOT See CDBootFileCreate
//See BootCdProbe().
      PrintF("%X: Pre Boot Blk\r\n",cur_blk);
      FWBlks(out_file,et,cur_blk<<2,4);
      cur_blk++;

      PrintF("%X: Boot Loader Blks\r\n",cur_blk);
      FWBlks(out_file,bootfile_buf,cur_blk<<2,bootfile_cd_blks<<2);
      cur_blk+=bootfile_cd_blks;
    }

    CDFileCreateDo(out_file,rootdir,
        &iso->root_dir_record,
        &iso2->root_dir_record,
        &cur_blk,rootdir,TRUE);

    tabbuf=CAlloc(tabsize*CD_BLK_SIZE);
    iso->type_l_path_table=cur_blk;
    tabbuf->name_len=2;  //Fill-in adam entry
    tempi=&iso->root_dir_record;
    tabbuf->blk=tempi->loc.little;
    tabbuf->parent_entry_num=1;
    tabbuf2=CAlloc(tabsize2*CD_BLK_SIZE);
    iso2->type_l_path_table=cur_blk+tabsize;
    tabbuf2->name_len=2;  //Fill-in adam entry
    tempi=&iso2->root_dir_record;
    tabbuf2->blk=tempi->loc.little;
    tabbuf2->parent_entry_num=1;
    itabbuf=tabbuf+1;
    itabbuf2=tabbuf2+1;
    first_free=2;
    for (i=1;i<=max_depth;i++)
      CDFillPathTable(rootdir,&itabbuf,&itabbuf2,
        1,FALSE,&first_free,1,i);
    PrintF("%X: Path Table 0\r\n",cur_blk);
    FWBlks(out_file,tabbuf,cur_blk<<2,tabsize<<2);
    cur_blk+=tabsize;
    PrintF("%X: Path Table 1\r\n",cur_blk);
    FWBlks(out_file,tabbuf2,cur_blk<<2,tabsize2<<2);
    cur_blk+=tabsize2;


    MemSet(tabbuf,0,tabsize*CD_BLK_SIZE);
    iso->type_m_path_table=BigEndianU32(cur_blk);
    tabbuf->name_len=2;  //Fill-in adam entry
    tempi=&iso->root_dir_record;
    tabbuf->blk=tempi->loc.big;
    tabbuf->parent_entry_num=BigEndianU16(1);
    MemSet(tabbuf2,0,tabsize2*CD_BLK_SIZE);
    iso2->type_m_path_table=BigEndianU32(cur_blk+tabsize);
    tabbuf2->name_len=2;  //Fill-in adam entry
    tempi=&iso2->root_dir_record;
    tabbuf2->blk=tempi->loc.big;
    tabbuf2->parent_entry_num=BigEndianU16(1);
    itabbuf=tabbuf+1;
    itabbuf2=tabbuf2+1;
    first_free=2;
    for (i=1;i<=max_depth;i++)
      CDFillPathTable(rootdir,&itabbuf,&itabbuf2,
        1,TRUE,&first_free,1,i);
    PrintF("%X: Path Table 2\r\n",cur_blk);
    FWBlks(out_file,tabbuf,cur_blk<<2,tabsize<<2);
    cur_blk+=tabsize;
    PrintF("%X: Path Table 3\r\n",cur_blk);
    FWBlks(out_file,tabbuf2,cur_blk<<2,tabsize2<<2);
    cur_blk+=tabsize2;

    LTDirListDel2(rootdir);
    FillU32Palindrome(&iso->vol_space_size,cur_blk);
    FillU32Palindrome(&iso2->vol_space_size,cur_blk);
    FWBlks(out_file,iso,16<<2,4);

    iso3->type=ISO1T_TERMINATOR;
    StrCpy(iso3->id,"CD001");
    iso3->version=1;
    if (bootfile_buf) {
      FWBlks(out_file,iso1,17<<2,4);
      FWBlks(out_file,iso2,18<<2,4);
      FWBlks(out_file,iso3,19<<2,4);
    } else {
      FWBlks(out_file,iso2,17<<2,4);
      FWBlks(out_file,iso3,18<<2,4);
    }
  } else
    cur_blk=0;

  FClose(out_file);
  Free(tabbuf);
  Free(tabbuf2);
done1:
  Free(bootfile_buf);
  Free(filename2);
  Free(et);
  Free(zero_buf);
  Free(iso);
  Free(iso1);
  Free(iso2);
  Free(iso3);
  return cur_blk;
}
#help_index ""
Historical capture: 2011-04-07. Home | Power on LoseThos | Sources and changes