mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-16 08:55:18 +00:00
added documentation; restored functionality of the source's iterator; tidied code
This commit is contained in:
parent
a7bdcd5792
commit
de1e5d2046
1 changed files with 80 additions and 18 deletions
|
|
@ -12,7 +12,7 @@ import scopt.OParser
|
||||||
import scala.collection.parallel.CollectionConverters._
|
import scala.collection.parallel.CollectionConverters._
|
||||||
import scala.io.{Codec, Source}
|
import scala.io.{Codec, Source}
|
||||||
import scala.sys.process._
|
import scala.sys.process._
|
||||||
import scala.util.{Try, Using}
|
import scala.util.Using
|
||||||
|
|
||||||
case class Config(
|
case class Config(
|
||||||
inDir: String = System.getProperty("user.dir"),
|
inDir: String = System.getProperty("user.dir"),
|
||||||
|
|
@ -41,7 +41,7 @@ object DecodePackets {
|
||||||
.text("Input directory"),
|
.text("Input directory"),
|
||||||
opt[Unit]('p', "preprocessed")
|
opt[Unit]('p', "preprocessed")
|
||||||
.action((_, c) => c.copy(preprocessed = true))
|
.action((_, c) => c.copy(preprocessed = true))
|
||||||
.text("Files are already preprocessed gcapy ascii files (do not call gcapy)"),
|
.text("Files are already preprocessed gcapy ASCII files"),
|
||||||
opt[Unit]('s', "skip-existing")
|
opt[Unit]('s', "skip-existing")
|
||||||
.action((_, c) => c.copy(skipExisting = true))
|
.action((_, c) => c.copy(skipExisting = true))
|
||||||
.text("Skip files that already exist in out-dir"),
|
.text("Skip files that already exist in out-dir"),
|
||||||
|
|
@ -91,6 +91,12 @@ object DecodePackets {
|
||||||
FileUtils.forceDelete(tmpFolder)
|
FileUtils.forceDelete(tmpFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerate over file names found in the given directory for later.
|
||||||
|
* @param directory where the files are found
|
||||||
|
* @param opts configuration entity where the files are collated
|
||||||
|
* @return configuration entity
|
||||||
|
*/
|
||||||
private def getAllFilesFromDirectory(directory: String, opts: Config): Config = {
|
private def getAllFilesFromDirectory(directory: String, opts: Config): Config = {
|
||||||
val inDir = Paths.get(directory)
|
val inDir = Paths.get(directory)
|
||||||
if (Files.exists(inDir) && Files.isDirectory(inDir)) {
|
if (Files.exists(inDir) && Files.isDirectory(inDir)) {
|
||||||
|
|
@ -108,28 +114,37 @@ object DecodePackets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary entry point into the process of parsing the packet capture files
|
||||||
|
* and producing the decoded packet data.
|
||||||
|
* Should be configurable for whatever state that the packet capture file can be structured.
|
||||||
|
* @param files all of the discovered files for consideration
|
||||||
|
* @param extension file extension being concatenated
|
||||||
|
* @param temporaryDirectory destination directory where files temporarily exist while being written
|
||||||
|
* @param outDirectory destination directory where the files are stored after being written
|
||||||
|
* @param skipExisting do not write a file that already exists at the destination directory
|
||||||
|
* @param readDecodeAndWrite next step of the file decoding process
|
||||||
|
*/
|
||||||
private def decodeFilesUsing(
|
private def decodeFilesUsing(
|
||||||
files: Seq[File],
|
files: Seq[File],
|
||||||
extension: String,
|
extension: String,
|
||||||
temporaryDirectory: File,
|
temporaryDirectory: File,
|
||||||
outDirectory: String,
|
outDirectory: String,
|
||||||
skipExisting: Boolean,
|
skipExisting: Boolean,
|
||||||
decodingFunc: (File,BufferedWriter)=>Try[List[String]]
|
readDecodeAndWrite: (File,BufferedWriter)=>Unit
|
||||||
): Unit = {
|
): Unit = {
|
||||||
files.par.foreach { file =>
|
files.par.foreach { file =>
|
||||||
val fileName = file.getName.split(extension)(0)
|
val fileName = file.getName.split(extension)(0)
|
||||||
val outFilePath = outDirectory + "/" + fileName + ".txt"
|
val fileNameWithExtension = fileName + ".txt"
|
||||||
|
val outFilePath = outDirectory + "/" + fileNameWithExtension
|
||||||
val outFile = new File(outFilePath)
|
val outFile = new File(outFilePath)
|
||||||
if (skipExisting && outFile.exists()) {
|
if (skipExisting && outFile.exists()) {
|
||||||
println(s"file $fileName skipped due to params")
|
println(s"file $fileName skipped due to params")
|
||||||
} else {
|
} else {
|
||||||
val tmpFilePath = temporaryDirectory.getAbsolutePath + "/" + fileName + ".txt"
|
val tmpFilePath = temporaryDirectory.getAbsolutePath + "/" + fileNameWithExtension
|
||||||
val writer = new BufferedWriter(new FileWriter(new File(tmpFilePath), false))
|
val writer = new BufferedWriter(new FileWriter(new File(tmpFilePath), false))
|
||||||
try {
|
try {
|
||||||
decodingFunc(file, writer).collect { lines =>
|
readDecodeAndWrite(file, writer)
|
||||||
println(s"${lines.size} lines read from file $fileName")
|
|
||||||
processAndRewriteFileContents(writer, lines)
|
|
||||||
}
|
|
||||||
writer.close()
|
writer.close()
|
||||||
Files.move(Paths.get(tmpFilePath), Paths.get(outFilePath), StandardCopyOption.REPLACE_EXISTING)
|
Files.move(Paths.get(tmpFilePath), Paths.get(outFilePath), StandardCopyOption.REPLACE_EXISTING)
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -142,22 +157,41 @@ object DecodePackets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def preprocessed(file: File, writer: BufferedWriter): Try[List[String]] = {
|
/**
|
||||||
|
* Read data from ASCII transcribed gcapy files.
|
||||||
|
* @param file file to read
|
||||||
|
* @param writer writer for output
|
||||||
|
*/
|
||||||
|
private def preprocessed(file: File, writer: BufferedWriter): Unit = {
|
||||||
Using(Source.fromFile(file.getAbsolutePath)(decoder)) { source =>
|
Using(Source.fromFile(file.getAbsolutePath)(decoder)) { source =>
|
||||||
source.getLines().toList
|
println(s"${decodeFileContents(writer, source.getLines())} lines read from file ${file.getName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def gcapy(file: File, writer: BufferedWriter): Try[List[String]] = {
|
/**
|
||||||
|
* Read data from gcapy files.
|
||||||
|
* @param file file to read
|
||||||
|
* @param writer writer for output
|
||||||
|
*/
|
||||||
|
private def gcapy(file: File, writer: BufferedWriter): Unit = {
|
||||||
Using(Source.fromString(s"gcapy -xa '${file.getAbsolutePath}'" !!)) { source =>
|
Using(Source.fromString(s"gcapy -xa '${file.getAbsolutePath}'" !!)) { source =>
|
||||||
source.getLines().toList
|
println(s"${decodeFileContents(writer, source.getLines())} lines read from file ${file.getName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode each line from the original file, decode it, then write it to the output file.
|
||||||
|
* @param writer writer for output
|
||||||
|
* @param lines raw packet data from the source
|
||||||
|
* @throws java.io.IOException if writing data goes incorrectly
|
||||||
|
* @return number of lines read from the source
|
||||||
|
*/
|
||||||
@throws(classOf[IOException])
|
@throws(classOf[IOException])
|
||||||
private def processAndRewriteFileContents(writer: BufferedWriter, lines: List[String]): Unit = {
|
private def decodeFileContents(writer: BufferedWriter, lines: Iterator[String]): Int = {
|
||||||
var linesToSkip = 0
|
var linesToSkip = 0
|
||||||
|
var linesRead: Int = 0
|
||||||
for (line <- lines.drop(1)) {
|
for (line <- lines.drop(1)) {
|
||||||
|
linesRead += 1
|
||||||
if (linesToSkip > 0) {
|
if (linesToSkip > 0) {
|
||||||
linesToSkip -= 1
|
linesToSkip -= 1
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -186,12 +220,25 @@ object DecodePackets {
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
linesRead
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Traverse down any nested packets such as SlottedMetaPacket, MultiPacket and MultiPacketEx and add indent for each layer down
|
/**
|
||||||
* The number of lines to skip will be returned so duplicate lines following SlottedMetaPackets in the gcapy output can be filtered out
|
* Traverse down any nested packets such as `SlottedMetaPacket`, `MultiPacket`, and `MultiPacketEx`
|
||||||
*/
|
* and add indent for each layer down.
|
||||||
private def recursivelyHandleNestedPacket(decodedLine: String, writer: BufferedWriter, depth: Int = 0): Int = {
|
* A number of lines to skip will be returned so duplicate lines following the nested packet can be filtered out.
|
||||||
|
* @param decodedLine decoded packet data
|
||||||
|
* @param writer writer for output
|
||||||
|
* @param depth the number of layers to indent
|
||||||
|
* @throws java.io.IOException if writing data goes incorrectly
|
||||||
|
* @return current indent layer
|
||||||
|
*/
|
||||||
|
@throws(classOf[IOException])
|
||||||
|
private def recursivelyHandleNestedPacket(
|
||||||
|
decodedLine: String,
|
||||||
|
writer: BufferedWriter,
|
||||||
|
depth: Int = 0
|
||||||
|
): Int = {
|
||||||
if (decodedLine.indexOf("Failed to parse") >= 0) return depth
|
if (decodedLine.indexOf("Failed to parse") >= 0) return depth
|
||||||
val regex = "(0x[a-f0-9]+)".r
|
val regex = "(0x[a-f0-9]+)".r
|
||||||
val matches = regex.findAllIn(decodedLine)
|
val matches = regex.findAllIn(decodedLine)
|
||||||
|
|
@ -213,6 +260,11 @@ object DecodePackets {
|
||||||
linesToSkip
|
linesToSkip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reformat data common to gcapy packet data files and their derivation form of ASCII transcription.
|
||||||
|
* @param line original string
|
||||||
|
* @return transformed string
|
||||||
|
*/
|
||||||
private def shortGcapyString(line: String): String = {
|
private def shortGcapyString(line: String): String = {
|
||||||
val regex = "Game record ([0-9]+) at ([0-9.]+s) is from ([S|C]).* to ([S|C]).*contents (.*)".r
|
val regex = "Game record ([0-9]+) at ([0-9.]+s) is from ([S|C]).* to ([S|C]).*contents (.*)".r
|
||||||
line match {
|
line match {
|
||||||
|
|
@ -222,11 +274,21 @@ object DecodePackets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A nested packet contains more packets.
|
||||||
|
* @param decodedLine decoded packet data
|
||||||
|
* @return `true`, if the packet is nested; `false`, otherwise
|
||||||
|
*/
|
||||||
private def isNestedPacket(decodedLine: String): Boolean = {
|
private def isNestedPacket(decodedLine: String): Boolean = {
|
||||||
// Also matches MultiPacketEx
|
// Also matches MultiPacketEx
|
||||||
decodedLine.indexOf("MultiPacket") >= 0 || decodedLine.indexOf("SlottedMetaPacket") >= 0
|
decodedLine.indexOf("MultiPacket") >= 0 || decodedLine.indexOf("SlottedMetaPacket") >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually decode the packet data.
|
||||||
|
* @param hexString raw packet data
|
||||||
|
* @return decoded packet data
|
||||||
|
*/
|
||||||
private def decodePacket(hexString: String): String = {
|
private def decodePacket(hexString: String): String = {
|
||||||
PacketCoding.decodePacket(ByteVector.fromValidHex(hexString)) match {
|
PacketCoding.decodePacket(ByteVector.fromValidHex(hexString)) match {
|
||||||
case Successful(value) => value.toString
|
case Successful(value) => value.toString
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue