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)); } // 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; } } }