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; } /// /// Get next token and move file pointer forward. /// /// true, search next token until it is found or all buffer has been checked;
false, only search next token in current line /// true for success, otherwise return false 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; } /// /// Peek next token and don't move file pointer /// Return true for success, otherwise return false /// /// true, search next token until it is found or all buffer has been checked; false, only search next token in current line 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)); } } }