/*********************************************************************************************
* Copyright 2021 - Volian Enterprises, Inc. All rights reserved.
* Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
* ------------------------------------------------------------------------------
* $Workfile: RO_FST.cs $ $Revision: 15 $
* $Author: Jsj $ $Date: 8/23/06 11:33a $
*
* $History: RO_FST.cs $
*
* ***************** Version 15 *****************
* User: Jsj Date: 8/23/06 Time: 11:33a
* Updated in $/EXE/RefObj/ROEditor
* debug statements commented out
*
* ***************** Version 14 *****************
* User: Jsj Date: 5/03/05 Time: 11:48a
* Updated in $/EXE/RefObj/ROEditor
* 2005 upgrade, move some ROFST logic to ROFST library
*
* ***************** Version 13 *****************
* User: Jsj Date: 5/11/04 Time: 9:29a
* Updated in $/EXE/RefObj/ROEditor
* Bug fix B2004-011, could not create FST file if user opened nodes on
* tree.
*
* ***************** Version 12 *****************
* User: Jsj Date: 4/08/04 Time: 9:48a
* Updated in $/EXE/RefObj/ROEditor
* Modified code to speed up the creation of the RO.FST file
*
* ***************** Version 11 *****************
* User: Jsj Date: 6/30/03 Time: 1:20p
* Updated in $/EXE/RefObj/ROEditor
* a NULL Accessory Page ID was giving problems in creating a new RO.FST
* file.
*
* ***************** Version 10 *****************
* User: Kathy Date: 6/11/03 Time: 2:01p
* Updated in $/EXE/RefObj/ROEditor
* Fix bug B2003-045
*
* ***************** Version 9 *****************
* User: Kathy Date: 5/30/03 Time: 12:48p
* Updated in $/EXE/RefObj/ROEditor
* B2003-044: sync up xml with UI tree view
*
* ***************** Version 8 *****************
* User: Kathy Date: 5/21/03 Time: 12:51p
* Updated in $/EXE/RefObj/ROEditor
* B2003-034: process data if only one field for RO & also, convert return
* value for xml save
*
* ***************** Version 7 *****************
* User: Jsj Date: 4/14/03 Time: 3:02p
* Updated in $/EXE/RefObj/ROEditor
* Speed up the creation of the RO.FST file
*
* ***************** Version 6 *****************
* User: Kathy Date: 4/04/03 Time: 9:41a
* Updated in $/EXE/RefObj/ROEditor
* B2003-030 convert new top group name to user readable for ro.fst
*
* ***************** Version 5 *****************
* User: Jsj Date: 2/21/03 Time: 9:51a
* Updated in $/EXE/RefObj/ROEditor
* added RO FST completed message
*
* ***************** Version 4 *****************
* User: Jsj Date: 1/02/03 Time: 9:31a
* Updated in $/EXE/RefObj/ROEditor
* Save Graphics file date in RO.FST file
*
* ***************** Version 3 *****************
* User: Jsj Date: 12/17/02 Time: 4:54p
* Updated in $/EXE/RefObj/ROEditor
* save real date for graphic files
*
* ***************** Version 2 *****************
* User: Jsj Date: 12/06/02 Time: 3:26p
* Updated in $/EXE/RefObj/ROEditor
* parameter display data fix (ingoring this data for now)
*
* ***************** Version 1 *****************
* User: Jsj Date: 11/27/02 Time: 12:53p
* Created in $/EXE/RefObj/ROEditor
* Modification to create the RO.FST file
*********************************************************************************************/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Xml;
using System.Xml.Schema;
using System.Text;
using System.IO;
using RODBInterface;
using System.Runtime.InteropServices;
using VlnStatus;
using System.Collections.Specialized;
using ROFST_FILE;
using Volian.Base.Library;
//using VlnProfiler; //don't forget to add VlnProfiler to the reference list
namespace ROEditor
{
///
/// This creates an RO.FST file
///
class FstTmpSTRC
{
public ushort thistype;
public uint thisoff;
public string title;
public FstTmpSTRC(ushort type, uint offset, string tl)
{
thistype = type;
thisoff = offset;
title = tl;
}
void WriteString(BinaryWriter bw, string str)
{
int n=str.Length;
byte [] b = new byte[n+1];
for(int i =0; i < n; i++)
{
b[i] = (byte)str[i];
}
bw.Write(b,0,n+1); // +1 to include null
/**
byte nullbyte = 0;
int i;
for(i =0; i < str.Length; i++)
{
byte WrByte;
WrByte = (byte)str[i];
bw.Write(WrByte);
}
bw.Write(nullbyte);
***/
}
public void Write(BinaryWriter bw)
{
bw.Write(thisoff);
bw.Write(thistype);
WriteString(bw,title);
}
}
// The Sorted Array was not sorting via the ASCII value of each character in a given string.
// This sorting function compares two strings by doing a character by character comparison.
public class MyComparer : IComparer
{
public int Compare (object x, object y)
{
int rtnval = 0;
int xcnt=0,ycnt=0;
int xlen,ylen;
byte xbyte, ybyte;
string xbuff = x.ToString();
string ybuff = y.ToString();
xlen = xbuff.Length;
ylen = ybuff.Length;
if (xbuff[0] == '<')
rtnval = 0;
if (ybuff[0] == '<')
rtnval = 0;
while ((rtnval==0) && ((xcnt < xlen) || (ycnt < ylen)))
{
xbyte = (xcnt == xlen)? (byte)0 : (byte)xbuff[xcnt++];
ybyte = (ycnt == ylen)? (byte)0 : (byte)ybuff[ycnt++];
rtnval = xbyte - ybyte;
}
return rtnval;
}
}
public class RO_FST
{
private FST_FileHeader FstHeader;
private string FstDir; // path to the RO directory
private string FstPath; // RO directory Path and RO.FST file name
private string FstOld; // ROFST.Old file name (backup of previous fst file)
private string FstNew; // ROFST.New hold the RO.FST info during creation
private BinaryWriter fhFST; // write handle to FST file being created
private VlnStatusBar StatusWin;
private RODB ROdatabase;
private XmlDocument FSTroXmlDoc;
// private HybridDictionary dicTiming;
// private HybridDictionary dicDuration;
ushort widestAcPgId;
SortedList IdsAndAccPgIds;
SortedList IdsAndOffsets;
ushort AllTypesUsed;
private Stack InUseListStack;
private Stack RtnValTmplateStack;
// private DateTime dtLast=DateTime.Now;
public RO_FST(RODB TheROdb, XmlDocument TheXmlDoc)
{
ROdatabase = TheROdb; // point to the RO database
FSTroXmlDoc = TheXmlDoc;
}
public bool Create()
{
bool RtnStat = true;
// dicDuration=new HybridDictionary();
// dicTiming=new HybridDictionary();
// Profiler.Reset();
// position to the top of the tree
// CurrentNode = roTree.TopNode;
// Setup the paths to the FST files
FstDir = Directory.GetCurrentDirectory();
FstPath = FstDir + "\\RO.FST";
FstOld = FstDir + "\\ROFST.Old";
FstNew = FstDir + "\\ROFST.New";
// open a temporary file for building the RO.FST
try
{
fhFST = new BinaryWriter(File.Open(FstNew,System.IO.FileMode.Create,System.IO.FileAccess.ReadWrite));
}
catch (Exception fhExc)
{
string errmsg = fhExc.Message;
MessageBox.Show(errmsg,"Error Creating RO.FST file",System.Windows.Forms.MessageBoxButtons.OK);
RtnStat = false;
}
// If we were able to open a new ROFST.NEW file then go ahead and put the
// new FST information into it.
if (RtnStat)
RtnStat = BuildNewRoFST();
if (RtnStat) // successful in creating new RO FST file?
{
//If an RO.FST file already exists,
// rename the existing file for safe keeping
if (File.Exists(FstPath))
{
// copy the existing RO.FST to ROFST.OLD
// - overrite ROFST.OLD if it already exists
File.Copy(FstPath,FstOld,true); // save RO.FST as ROFST.OLD
File.Delete(FstPath); // remove RO.FST
}
File.Copy(FstNew,FstPath,true); // save ROFST.NEW as RO.fST
File.Delete(FstNew); // remove ROFST.NEW
if (OrphanedRecords.Length > 0)
{
using (StreamWriter sw = new StreamWriter(Path.Combine(FstDir, @"Orphaned RO Records.txt"), false))
{
sw.Write(OrphanedRecords.ToString());
sw.Close();
}
MessageBox.Show("The file Orphaned RO Records.txt has been created", "Warning - Orphan RO Record");
}
MessageBox.Show("New RO.FST file created successfully.","Create RO.FST file",System.Windows.Forms.MessageBoxButtons.OK);
}
// MessageBox.Show(Profiler.ToString("000.00%"),"Timings");
// dicDuration=null;
// dicTiming=null;
return RtnStat;
}
private bool BuildNewRoFST()
{
bool RtnVal = true;
int i;
int numDatabases;
// write the empty header - save space for the header info
FstHeader = new FST_FileHeader();
FstHeader.Write(fhFST);
// point to the ROMASTER and get the number of databases it has
VlnXmlElement ROdbtables = (VlnXmlElement) FSTroXmlDoc.FirstChild;
numDatabases = ROdbtables.ChildNodes.Count;
// Allocate a list of database info structures
ROFST_DbInfo[] dbinfo = new ROFST_DbInfo[numDatabases];
//For each RO database, process each group
XmlNode ROtableNode = ROdbtables;
ROtableNode = ROtableNode.FirstChild;
StatusWin = new VlnStatusBar("Creating RO.FST file");
for (i=0; i < numDatabases && RtnVal; i++)
{
dbinfo[i] = new ROFST_DbInfo();
RtnVal = SaveToFSTFile(ROtableNode,dbinfo[i]);
dbinfo[i].dbiType = AllTypesUsed;
ROtableNode = ROtableNode.NextSibling;
}
if (RtnVal)
{
VlnXmlElement tblnode = (VlnXmlElement) ROdbtables.FirstChild;
StatusWin.BarMax = numDatabases * 2;
StatusWin.BarStepValue = 1;
StatusWin.BarValue = 0;
// All of the RO database, group, and return value information
// was saved to the FST file. Now save the information needed
// to read the FST file.
// Save DB list
uint lngbuf = 0;
uint dblength = (uint)fhFST.BaseStream.Position;
fhFST.Write(lngbuf);
ushort intbuf = Convert.ToUInt16(numDatabases);
fhFST.Write(intbuf);
// The old code did a sizeof() on a structure containing the ROFSTDatabaseInfo (dbi)
// You cannot read/write a Struct type in C# like you can with C++. The C# books suggest
// creating a Class to replace the Struct, then create methods that perform the binary
// read/write. So I created a method that calculates the "struct" size to simulate the
// sizeof(struct dbi)
uint lngval = (dbinfo[0].GetStructSize()) * (uint)numDatabases;
for (i=0; i=0)
{
outstr += tmpstr.Substring(cur,indx-cur);
asc_spchar = tmpstr.Substring(indx+3,3);
decval = System.Convert.ToInt16(asc_spchar,10);
outstr += System.Convert.ToChar(decval).ToString();
cur = indx+6;
if (cur+6 > len)
indx = -1;
else
indx = tmpstr.IndexOf(OKpunch,cur);
}
if (cur= 1) && !TheMenuTitle.Equals(""))
{
curOffset = (uint)fhFST.BaseStream.Position;
curType = SaveROToFST(chldnode,InUseList,RtnValTmplate,AccPageIDTplate);
}
else
{
SkipThisOne = true;
}
}
if (!SkipThisOne)
{
string tmpstr = ChildElem.GetAttribute("MenuTitle");
tmpfststrc = new FstTmpSTRC(curType,curOffset,tmpstr);
FstTmp.Add(tmpfststrc);
typ |= curType;
numgroups++;
}
} // end if VlnXmlElement
// Get next child
chldnode = chldnode.NextSibling;
}// end while
} // if child is not null
// pop the InUseList stack
if (PopInUseStack)
InUseListStack.Pop();
// pop the return valuse template stack
if (PopRtnValStack)
RtnValTmplateStack.Pop();
// save the current position of the FST file for the return value
RtnVal = (uint)fhFST.BaseStream.Position;
// Save the ID and offset entry for the current ID
IdsAndOffsets.Add(curRecID,RtnVal);
fhFST.Write(curRecID);
fhFST.Write(elemParentID);
fhFST.Write(numgroups);
// write the FstTmp
for (int i=0; i < FstTmp.Count; i++)
{
tmpfststrc = (FstTmpSTRC)FstTmp[i];
tmpfststrc.Write(fhFST);
}
FstTmp.Clear();
return RtnVal;
}
public void WriteGraphicsReturnValue(string OrigStr)
{
string text, tmptext, hgttxt, widtxt, imgdateStr;
int idx;
int hgt, wid;
int imgdateInt;
byte newline = 10;
char[] tmpbuf = OrigStr.ToCharArray();
// remove the trailing "\r\n"
tmptext = OrigStr.Substring(0,OrigStr.Length-2);
// parse out the image width
idx = tmptext.LastIndexOf("\r\n");
widtxt = tmptext.Substring(idx+2,tmptext.Length-(idx+2));
wid = Convert.ToInt32(widtxt,10);
widtxt = wid.ToString("x4");
tmptext = tmptext.Substring(0,idx);
// parse out the image height
idx = tmptext.LastIndexOf("\r\n");
hgttxt = tmptext.Substring(idx+2,tmptext.Length-(idx+2));
hgt = Convert.ToInt32(hgttxt,10);
hgttxt = hgt.ToString("x4");
tmptext = tmptext.Substring(0,idx);
// parse out the image file name and image date
idx = tmptext.IndexOf(" ");
text = tmptext.Substring(idx+1);
if (tmptext.StartsWith("\r\n"))
imgdateStr = tmptext.Substring(2,idx-2); // first 2 chars are "\r\n"
else
{
if(idx < 1)
imgdateStr=null;
else
imgdateStr = tmptext.Substring(0,idx);
}
if (imgdateStr == null|| imgdateStr=="")
imgdateInt = 0;
else
imgdateInt = Convert.ToInt32(imgdateStr,10);
// write graphics file name
WriteString(text,false);
fhFST.Write(newline);
// write dummy date (old FST file had only zeros!
// WriteString("00000000",false);
WriteString(imgdateInt.ToString("x8"),false);
fhFST.Write(newline);
// write the image height
WriteString(hgttxt,false);
fhFST.Write(newline);
//write the image width
WriteString(widtxt,true);
}
private ushort SaveROToFST(XmlNode RONode,ArrayList InUseList,string RtnValTmplate, string AccPageIDTplate)
{
ushort RtnVal;
uint startFST = (uint)fhFST.BaseStream.Position;
uint RORecID;
uint ParID;
byte nullbyte=0;
VlnXmlElement elem;
elem = (VlnXmlElement) RONode;
// string dummy = ""; // need for RODB_GetFIeldsInUse call, won't be used.
// ArrayList AvailList, InUseList;
// //Get the "In Use" field list
// AvailList = ROdatabase.RODB_GetFields(elem, (uint) RecordType.Schema);
// InUseList = ROdatabase.RODB_GetFieldsInUse(elem, AvailList,"FieldsInUse", ref dummy);
//Write the RO's record ID
string RecIdStr = elem.GetAttribute("RecID");
RORecID = System.Convert.ToUInt32(RecIdStr,16);// B2019-105 Corrected conversion to 32 bit
fhFST.Write(RORecID);
// **** Debug
// if (RecIdStr.Equals("000000a6"))
// nullbyte=0;
// ***
// Get the Accessory Page ID
// string AccPageIDTplate = elem.GetAccPageIDTemplate();
string AccPageID = elem.GetAccPageIDString(AccPageIDTplate);
// **** Debug
// if (AccPageID.Equals("S:1"))
// nullbyte=0;
// *****
//Write the parent ID
string ParIDstr = elem.GetAttribute("ParentID");
ParID = System.Convert.ToUInt32(ParIDstr,16);// B2019-104 Corrected to 32 Bit
fhFST.Write(ParID);
// not sure why, was in old FST code but no comment!
// I assume that it's some sort of separater
short none = -1;
fhFST.Write(none);
//Get the RO Return value and return type
// string RtnValTmplate = elem.GetReturnValueTemplate();
RtnVal = 0;
string tablename = elem.GetAttribute("Table");
// if (RtnValTmplate.Equals(""))
// {
// int i;
// i =0;
// }
string cvttmp= CvtFldToUserFld(RtnValTmplate);
string RORtnVal = elem.GetReturnValue(ROdatabase,tablename,cvttmp,InUseList,ref RtnVal,ROEditor.Form1.PCChildren); // C2021-026 pass in P/C Children
// Write the field type to the FST
fhFST.Write(RtnVal);
if (RtnVal == 8) // is this an Image (graphics) record?
WriteGraphicsReturnValue(RORtnVal);
else
WriteString(RORtnVal);
// Write the Accessory Page ID
// Fix for Bug B2003-039. Added check for a NULL AccPageID
// If is null, then assign a blank string
if (AccPageID != null)
AccPageID = AccPageID.Trim();
else
AccPageID = " ";
WriteString(AccPageID);
// Save the ID and offset entry for the current ID
IdsAndOffsets.Add(RORecID,startFST);
// Save the RecID and Accessory Page id
IdsAndAccPgIds[AccPageID] = RORecID;
// Save the widest AccPageID width
int acclen = AccPageID.Length;
if (acclen > widestAcPgId)
widestAcPgId = (ushort)acclen;
// if(dtLast.AddSeconds(.3) < DateTime.Now)
// {
StatusWin.StatMsg = AccPageID;
StatusWin.PerformStep();
// dtLast=DateTime.Now;
// }
return RtnVal;
}
public void WriteString(string str)
{
WriteString(str,true);
}
public void WriteString(string str,bool AddNull)
{
int n=str.Length;
if (AddNull)
n++;
byte [] b = new byte[n];
for(int i =0; i < str.Length; i++)
{
b[i] = (byte)str[i];
}
fhFST.Write(b,0,n);
}
}
}