//+------------------------------------------------------------------+ //| TkzArchive.mqh | //| .tkz archive files: a 64-byte file header followed by TKZ1 | //| chunks (see TkzCodec.mqh and docs/FORMAT.md). | //+------------------------------------------------------------------+ #ifndef TKZ_ARCHIVE_MQH #define TKZ_ARCHIVE_MQH #include "TkzCodec.mqh" #define TKZ_FILE_MAGIC 0x465A4B54 // bytes "TKZF" #define TKZ_FILE_VERSION 1 #define TKZ_FILE_HEADER_SIZE 64 #define TKZ_SYMBOL_BYTES 32 //+------------------------------------------------------------------+ //| Little-endian helpers shared by writer and reader | //+------------------------------------------------------------------+ class CTkzBytes { public: void PutU16(uchar &dst[],const int pos,const int v) { dst[pos]=(uchar)(v&255); dst[pos+1]=(uchar)((v>>8)&255); } void PutU32(uchar &dst[],const int pos,const uint v) { dst[pos]=(uchar)(v&255); dst[pos+1]=(uchar)((v>>8)&255); dst[pos+2]=(uchar)((v>>16)&255); dst[pos+3]=(uchar)((v>>24)&255); } int GetU16(const uchar &src[],const int pos) { return (int)src[pos]|((int)src[pos+1]<<8); } uint GetU32(const uchar &src[],const int pos) { return (uint)src[pos]|((uint)src[pos+1]<<8)|((uint)src[pos+2]<<16)|((uint)src[pos+3]<<24); } }; //+------------------------------------------------------------------+ //| Writer: creates (or appends to) a .tkz file | //+------------------------------------------------------------------+ class CTkzWriter { private: int m_h; CTkzCodec m_codec; CTkzCodec m_check; CTkzBytes m_io; uchar m_buf[]; string m_error; long m_ticks; long m_bytes; bool m_verify; ulong m_enc_us; ulong m_ver_us; long m_vt[]; double m_vb[]; double m_va[]; public: CTkzWriter(void) { m_h=INVALID_HANDLE; m_error=""; m_ticks=0; m_bytes=0; m_verify=false; m_enc_us=0; m_ver_us=0; } ~CTkzWriter(void) { Close(); } string LastError(void) { return m_error; } long TicksWritten(void) { return m_ticks; } long BytesWritten(void) { return m_bytes; } ulong EncodeMicroseconds(void) { return m_enc_us; } ulong VerifyMicroseconds(void) { return m_ver_us; } //--- decode every written chunk again and compare it with the input void SetVerify(const bool verify) { m_verify=verify; } //--- create a new file (or append chunks to an existing one when append=true) bool Open(const string filename,const string symbol,const int digits,const bool common,const bool append=false) { Close(); m_error=""; m_ticks=0; m_bytes=0; int flags=FILE_BIN|FILE_WRITE; if(common) flags|=FILE_COMMON; if(append && FileIsExist(filename,common ? FILE_COMMON : 0)) { m_h=FileOpen(filename,flags|FILE_READ); if(m_h==INVALID_HANDLE) { m_error="cannot open file for append"; return false; } uchar old[]; ArrayResize(old,TKZ_FILE_HEADER_SIZE); if((int)FileReadArray(m_h,old,0,TKZ_FILE_HEADER_SIZE)!=TKZ_FILE_HEADER_SIZE || m_io.GetU32(old,0)!=(uint)TKZ_FILE_MAGIC) { m_error="existing file is not a TKZ archive"; FileClose(m_h); m_h=INVALID_HANDLE; return false; } FileSeek(m_h,0,SEEK_END); m_bytes=(long)FileTell(m_h); return true; } m_h=FileOpen(filename,flags); if(m_h==INVALID_HANDLE) { m_error="cannot create file"; return false; } uchar hdr[]; ArrayResize(hdr,TKZ_FILE_HEADER_SIZE); ArrayInitialize(hdr,0); m_io.PutU32(hdr,0,(uint)TKZ_FILE_MAGIC); m_io.PutU16(hdr,4,TKZ_FILE_VERSION); m_io.PutU16(hdr,6,TKZ_FILE_HEADER_SIZE); uchar name[]; int len=StringToCharArray(symbol,name,0,-1,CP_UTF8); for(int i=0; ifsize) { m_error="truncated last chunk"; break; } ArrayResize(m_offset,m_count+1,256); ArrayResize(m_info,m_count+1,256); m_offset[m_count]=pos; m_info[m_count]=info; m_count++; pos+=info.total_size; } return true; } void Close(void) { if(m_h!=INVALID_HANDLE) { FileClose(m_h); m_h=INVALID_HANDLE; } m_count=0; ArrayFree(m_offset); ArrayFree(m_info); ArrayFree(m_buf); m_codec.Release(); } bool GetChunkInfo(const int i,STkzChunkInfo &info) { if(i<0 || i>=m_count) return false; info=m_info[i]; return true; } long TickCount(void) { long n=0; for(int i=0; i=m_count || m_h==INVALID_HANDLE) { m_error="bad chunk index"; return -1; } int sz=m_info[i].total_size; ArrayResize(m_buf,sz); if(!FileSeek(m_h,m_offset[i],SEEK_SET) || (int)FileReadArray(m_h,m_buf,0,sz)!=sz) { m_error="read error"; return -1; } int n=m_codec.DecodeTicks(m_buf,0,ticks,append); if(n<0) m_error=m_codec.LastError(); return n; } //--- all ticks of the archive int ReadAll(MqlTick &ticks[]) { ArrayResize(ticks,0); for(int i=0; i=to_msc) continue; int n=ReadChunk(i,tmp,false); if(n<0) return -1; ArrayResize(ticks,total+n,n); for(int k=0; k=from_msc && tmp[k].time_msc