{"record":{"id":"7e030762a785cd82","repo":"OpenRA/OpenRA","slug":"file-name-table-in-meg-file-inconsistent","errorCode":null,"errorMessage":"File name table in .meg file inconsistent","messagePattern":"File name table in \\.meg file inconsistent","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"OpenRA.Mods.Cnc/FileSystem/MegFile.cs","lineNumber":84,"sourceCode":"\n\t\t\t\tvar headerSize = s.ReadUInt32();\n\t\t\t\tvar numStrings = s.ReadUInt32();\n\t\t\t\tvar numFiles = s.ReadUInt32();\n\t\t\t\tvar stringsSize = s.ReadUInt32();\n\t\t\t\tvar stringsStart = s.Position;\n\n\t\t\t\tvar filenames = new List<string>();\n\n\t\t\t\t// The file names are an indexed array of strings\n\t\t\t\tfor (var i = 0; i < numStrings; i++)\n\t\t\t\t{\n\t\t\t\t\tvar length = s.ReadUInt16();\n\t\t\t\t\tfilenames.Add(s.ReadASCII(length));\n\t\t\t\t}\n\n\t\t\t\t// The header indicates where we should be, so verify it\n\t\t\t\tif (s.Position != stringsSize + stringsStart)\n\t\t\t\t\tthrow new Exception(\"File name table in .meg file inconsistent\");\n\n\t\t\t\t// Now we load each file entry and associated info\n\t\t\t\tcontents = new Dictionary<string, (uint Offset, int Length)>((int)numFiles);\n\t\t\t\tfor (var i = 0; i < numFiles; i++)\n\t\t\t\t{\n\t\t\t\t\t// Ignore flags, crc, index\n\t\t\t\t\ts.Position += 10;\n\t\t\t\t\tvar size = s.ReadUInt32();\n\t\t\t\t\tvar offset = s.ReadUInt32();\n\t\t\t\t\tvar nameIndex = s.ReadUInt16();\n\t\t\t\t\tcontents[filenames[nameIndex]] = (offset, (int)size);\n\t\t\t\t}\n\n\t\t\t\tcontents.TrimExcess();\n\n\t\t\t\tif (s.Position != headerSize)\n\t\t\t\t\tthrow new Exception(\"Expected to be at data start offset\");\n\t\t\t}","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/OpenRA/OpenRA/blob/a520984d91eda9de48a62b1d15c1e3bad0d4fb1a/OpenRA.Mods.Cnc/FileSystem/MegFile.cs#L66-L102","documentation":"Thrown by MegFile after reading the filename string table, when the stream position does not equal stringsStart + stringsSize. The header declares the total byte size of the filename table; after reading numStrings entries the parser validates it landed exactly where expected. A mismatch indicates the file is internally inconsistent — either the declared sizes are wrong or string lengths are misaligned.","triggerScenarios":"After the loop reading numStrings entries (each a uint16 length + ASCII string), s.Position != stringsSize + stringsStart. Triggered when mounting a .meg archive whose filename table is malformed.","commonSituations":"The .meg file was produced by a packing tool that mislabeled string lengths or counts; the file uses a different string encoding (e.g. UTF-16 vs ASCII); the file is truncated within the filename table region; byte-order differences between platforms.","solutions":["Re-pack the .meg archive using a known-compatible tool (e.g. the original game's toolchain or a community MEG editor).","Compare the file against a known-good .meg from the same game to detect structural differences.","Inspect the header fields (numStrings, stringsSize) in a hex editor against the actual data layout.","Replace the archive with an original, unmodified copy."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// After reading filename strings, validate position\nif (s.Position != stringsSize + stringsStart)\n    // The MEG filename table is corrupted; reject the archive","typeGuard":null,"tryCatchPattern":"try { meg = new MegFile(stream, filename); } catch (Exception ex) when (ex.Message.Contains(\"inconsistent\")) { /* handle corrupt MEG */ }","preventionTips":["Use well-tested MEG packing tools.","Keep original game archives unmodified.","Validate file integrity after any repacking operation."],"tags":["archive","file-format","meg","parsing","consistency-check"],"backgroundTag":null,"analyzedSha":"a520984d91eda9de48a62b1d15c1e3bad0d4fb1a","analyzedAt":"2026-08-13T15:30:14.787Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}