260 lines
9.8 KiB
C#
260 lines
9.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using ModelRenderer.Scripts.Common;
|
|
|
|
namespace BrewMonster.Common
|
|
{
|
|
|
|
public struct SCRIPTINFO
|
|
{
|
|
public byte[] pFileBuf; // Pointer to file data buffer
|
|
public byte[] pStart; // Start address of buffer
|
|
public byte[] pEnd; // End address of buffer
|
|
public byte[] pCur; // Current pointer. Pointer size is 2 bytes
|
|
public uint pCurIndex; // Current pointer index
|
|
public int iLine; // Line counter
|
|
}
|
|
|
|
public class AWScriptFile
|
|
{
|
|
private const int MAX_LINELEN = 2048;
|
|
public ushort[] m_szToken = new ushort[MAX_LINELEN];
|
|
|
|
protected SCRIPTINFO m_Script = new SCRIPTINFO();
|
|
|
|
|
|
|
|
public bool Open(FileStream stream)
|
|
{
|
|
uint dwFilelen = (uint)stream.Length;
|
|
if (dwFilelen <= 0)
|
|
{
|
|
BMLogger.LogError($"EC_StringTab::Open: {stream.Name} File length is 0");
|
|
return false;
|
|
}
|
|
|
|
byte[] pBuf = new byte[dwFilelen];
|
|
|
|
stream.Read(pBuf, 0, (int)dwFilelen);
|
|
|
|
// Check unicode file header
|
|
ushort wChar = BitConverter.ToUInt16(new byte[] {pBuf[0], pBuf[1]});
|
|
if (wChar != 0xfeff)
|
|
{
|
|
BMLogger.LogError($"EC_StringTab::Open: {stream.Name} File is not unicode");
|
|
return false;
|
|
}
|
|
|
|
m_Script.pFileBuf = pBuf;
|
|
m_Script.pStart = new []{m_Script.pFileBuf[2], m_Script.pFileBuf[3]}; // Skip unicode magic number
|
|
m_Script.pCur = m_Script.pStart;
|
|
m_Script.pEnd = new [] {m_Script.pFileBuf[pBuf.Length - 2], m_Script.pFileBuf[pBuf.Length - 1]};
|
|
m_Script.pCurIndex = 2;
|
|
m_Script.iLine = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool Open(string szFile)
|
|
{
|
|
if (!File.Exists(szFile))
|
|
{
|
|
BMLogger.LogError($"EC_StringTab::Open: {szFile} File not found");
|
|
return false;
|
|
}
|
|
|
|
using (FileStream stream = new FileStream(szFile, FileMode.Open, FileAccess.Read))
|
|
{
|
|
var bRet = Open(stream);
|
|
if (!bRet)
|
|
{
|
|
BMLogger.LogError($"EC_StringTab::Open: {szFile} File open failed");
|
|
return false;
|
|
}
|
|
stream.Close();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
//TODO: May be not needed
|
|
m_Script.pFileBuf = null;
|
|
m_Script.pStart = null;
|
|
m_Script.pCur = null;
|
|
m_Script.pEnd = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get next token and move file pointer forward.
|
|
/// </summary>
|
|
/// <param name="bCrossLine">true, search next token until it is found or all buffer has been checked;<br/> false, only search next token in current line</param>
|
|
/// <returns>true for success, otherwise return false</returns>
|
|
public bool GetNextToken(bool bCrossLine)
|
|
{
|
|
NewLine:
|
|
while (m_Script.pCurIndex < m_Script.pFileBuf.Length)
|
|
{
|
|
// get the first byte because the pointer is 2 bytes. The second byte should be 0 in these cases (value < 255)
|
|
if (m_Script.pFileBuf[m_Script.pCurIndex] > 32 && m_Script.pFileBuf[m_Script.pCurIndex] != ';' &&
|
|
m_Script.pFileBuf[m_Script.pCurIndex] != ',')
|
|
break;
|
|
|
|
m_Script.pCurIndex += 2; // move pointer forward 2 bytes because it's w_char (2 bytes)
|
|
if (m_Script.pCurIndex >= m_Script.pFileBuf.Length)
|
|
return false;
|
|
m_Script.pCur[0] = m_Script.pFileBuf[m_Script.pCurIndex];
|
|
m_Script.pCur[1] = m_Script.pFileBuf[m_Script.pCurIndex + 1];
|
|
if (m_Script.pCur[0] == '\n')
|
|
{
|
|
if (!bCrossLine)
|
|
{
|
|
m_Script.pCurIndex -= 2; // Let search pointer still stop in this line
|
|
return false;
|
|
}
|
|
|
|
m_Script.iLine++;
|
|
}
|
|
}
|
|
|
|
if (m_Script.pCurIndex >= m_Script.pFileBuf.Length)
|
|
return false;
|
|
|
|
// Skip comment lines those begin with '//'
|
|
if (m_Script.pFileBuf[m_Script.pCurIndex] == '/' && m_Script.pFileBuf[m_Script.pCurIndex + 2] == '/')
|
|
{
|
|
// This is a note line, search it's ending.
|
|
while (!m_Script.pCur.Equals(m_Script.pEnd) && m_Script.pFileBuf[m_Script.pCurIndex] != '\n')
|
|
{
|
|
m_Script.pCurIndex += 2;
|
|
}
|
|
|
|
if (m_Script.pCurIndex >= m_Script.pFileBuf.Length) // Found nothing
|
|
return false;
|
|
if (!bCrossLine) // Don't search cross line
|
|
return false;
|
|
|
|
m_Script.pCurIndex += 2; // Skip '\n'
|
|
m_Script.iLine++;
|
|
goto NewLine;
|
|
}
|
|
|
|
// Text between /* */ are also comment
|
|
if (m_Script.pFileBuf[m_Script.pCurIndex] == '/' && m_Script.pFileBuf[m_Script.pCurIndex + 2] == '*')
|
|
{
|
|
bool bError = false;
|
|
|
|
m_Script.pCurIndex += 4; // Skip /*
|
|
|
|
while (m_Script.pFileBuf[m_Script.pCurIndex] != '*' || m_Script.pFileBuf[m_Script.pCurIndex + 2] != '/')
|
|
{
|
|
if (m_Script.pCurIndex >= m_Script.pEnd.Length) // Found nothing
|
|
return false;
|
|
else if (m_Script.pFileBuf[m_Script.pCurIndex] == '\n')
|
|
{
|
|
if (!bCrossLine)
|
|
{
|
|
// This is a fatal error, we should return false.
|
|
// But we must search the '*/' so that next time our begin point
|
|
// isn't in comment paragraph
|
|
bError = true;
|
|
}
|
|
|
|
m_Script.iLine++;
|
|
}
|
|
|
|
m_Script.pCurIndex += 2;
|
|
}
|
|
|
|
m_Script.pCurIndex += 4; // Skip */
|
|
|
|
if (bError)
|
|
return false;
|
|
|
|
goto NewLine;
|
|
}
|
|
|
|
int i = 0;
|
|
|
|
// Copy string in "" or () pair
|
|
if (m_Script.pFileBuf[m_Script.pCurIndex] == '"' || m_Script.pFileBuf[m_Script.pCurIndex] == '(')
|
|
{
|
|
char cEnd = m_Script.pFileBuf[m_Script.pCurIndex] == '"' ? '"' : ')';
|
|
|
|
// Quoted token
|
|
m_Script.pCurIndex += 2; // Skip " or (
|
|
m_Script.pCur[0] = m_Script.pFileBuf[m_Script.pCurIndex];
|
|
m_Script.pCur[1] = m_Script.pFileBuf[m_Script.pCurIndex + 1];
|
|
|
|
while (!m_Script.pCur.Equals(m_Script.pEnd) && m_Script.pFileBuf[m_Script.pCurIndex] != cEnd)
|
|
{
|
|
if (i >= MAX_LINELEN-1)
|
|
return false;
|
|
|
|
// save the text into the token array
|
|
m_szToken[i++] = BitConverter.ToUInt16(m_Script.pCur);
|
|
|
|
m_Script.pCurIndex += 2;
|
|
m_Script.pCur[0] = m_Script.pFileBuf[m_Script.pCurIndex];
|
|
m_Script.pCur[1] = m_Script.pFileBuf[m_Script.pCurIndex + 1];
|
|
}
|
|
|
|
m_Script.pCurIndex += 2; // Skip " or )
|
|
m_Script.pCur[0] = m_Script.pFileBuf[m_Script.pCurIndex];
|
|
m_Script.pCur[1] = m_Script.pFileBuf[m_Script.pCurIndex + 1];
|
|
}
|
|
else // Is a normal token
|
|
{
|
|
while (!m_Script.pCur.Equals(m_Script.pEnd) && BitConverter.ToInt16(m_Script.pCur) > 32 &&
|
|
m_Script.pFileBuf[m_Script.pCurIndex] != ';' && m_Script.pFileBuf[m_Script.pCurIndex] != ',')
|
|
{
|
|
if (i >= MAX_LINELEN-1)
|
|
return false;
|
|
|
|
m_szToken[i++] = BitConverter.ToUInt16(m_Script.pCur);
|
|
|
|
m_Script.pCurIndex += 2;
|
|
m_Script.pCur[0] = m_Script.pFileBuf[m_Script.pCurIndex];
|
|
m_Script.pCur[1] = m_Script.pFileBuf[m_Script.pCurIndex + 1];
|
|
}
|
|
}
|
|
|
|
m_szToken[i] = '\0';
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Peek next token and don't move file pointer
|
|
/// Return true for success, otherwise return false
|
|
/// </summary>
|
|
/// <param name="bCrossLine">true, search next token until it is found or all buffer has been checked; false, only search next token in current line</param>
|
|
public bool PeekNextToken(bool bCrossLine)
|
|
{
|
|
// Record current pointer and line
|
|
var pCur = new [] {m_Script.pCur[0], m_Script.pCur[1]};
|
|
var iLine = m_Script.iLine;
|
|
var pCurIndex = m_Script.pCurIndex;
|
|
|
|
bool bRet = GetNextToken(bCrossLine);
|
|
|
|
// Restore pointer and line
|
|
m_Script.pCur = pCur;
|
|
m_Script.iLine = iLine;
|
|
m_Script.pCurIndex = pCurIndex;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
public int GetNextTokenAsInt(bool bCrossLine)
|
|
{
|
|
GetNextToken(bCrossLine);
|
|
return int.Parse(ByteToStringUtils.UshortArrayToUnicodeString(m_szToken));
|
|
}
|
|
|
|
// Reach end of file ?
|
|
public bool IsEnd() { return m_Script.pCurIndex >= m_Script.pFileBuf.Length; }
|
|
// Get current line
|
|
public int GetCurLine() { return m_Script.iLine; }
|
|
}
|
|
} |