Files
test/Assets/PerfectWorld/Scripts/Common/AWScriptFile.cs
2026-03-09 19:28:45 +07:00

321 lines
12 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; }
/* Search specified token. This function get next token and check whether it match
specified string, if match, then stop and return true, otherwise get next token
again until all file is checked or token is found.
Note: This will be crossing-line search.
Return true for success, otherwise return false.
wszToken: specified token will be searched
bCaseSensitive: true, case sensitive
*/
public bool MatchToken(ushort[] wszToken, bool bCaseSensitive)
{
while (GetNextToken(true))
{
if (bCaseSensitive)
{
if (string.Compare(ByteToStringUtils.UshortArrayToUnicodeString(m_szToken),
ByteToStringUtils.UshortArrayToUnicodeString(wszToken),
StringComparison.Ordinal) == 0)
{
return true;
}
}
else
{
if (string.Compare(ByteToStringUtils.UshortArrayToUnicodeString(m_szToken),
ByteToStringUtils.UshortArrayToUnicodeString(wszToken),
StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
}
}
return false;
}
public bool MatchToken(string wszToken, bool bCaseSensitive)
{
while (GetNextToken(true))
{
if (bCaseSensitive)
{
if (string.Compare(ByteToStringUtils.UshortArrayToUnicodeString(m_szToken),
wszToken, StringComparison.Ordinal) == 0)
{
return true;
}
}
else
{
if (string.Compare(ByteToStringUtils.UshortArrayToUnicodeString(m_szToken),
wszToken, StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
}
}
return false;
}
}
}