Compare commits

...

49 Commits

Author SHA1 Message Date
feyris-tan
3668cad724 Patch overdumped MPEG-DASH segments.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 3m25s
2025-11-23 21:23:41 +01:00
feyris-tan
30026b2b02 Added functionality necessary to extract the MPEG-DASH segments on the Arsat stream.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 3m38s
2025-11-23 00:27:00 +01:00
feyris-tan
c0f644df1a Ditched xUnit and put in MSTest for automated testing instead.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 7s
2025-11-19 17:46:17 +01:00
feyris-tan
36969eede2 Refactored Tests.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 2m4s
2025-11-17 22:13:31 +01:00
feyris-tan
bc6d9afa52 Added a catalogue generator option.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m24s
2025-11-17 22:08:38 +01:00
feyris-tan
92f787bbd4 Fix a bug in the detection of BBC News in the BFBS Stream.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m7s
2025-11-14 22:25:51 +01:00
feyris-tan
eb2ceb5f72 Extract BBC News from BFBS stream.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m10s
2025-11-13 22:24:37 +01:00
feyris-tan
4786e9865c Detect the second BFBS stream.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m5s
2025-11-12 21:34:21 +01:00
feyris-tan
af8edceca2 Actually scrape LogonResponseDescriptor, LowerLayerServiceDescriptor and HigherLayersInitializeDescriptor.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m9s
2025-11-12 17:36:42 +01:00
feyris-tan
8e3311bcce Actually scrape the SatelliteReturnLinkDescriptor
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m8s
2025-11-11 21:19:38 +01:00
feyris-tan
2c4bc0a558 Added support for LogonResponseDescriptor, LowerLayerServiceDescriptor, HigherLayersInitializeDescriptor.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m2s
2025-11-10 20:04:56 +01:00
feyris-tan
e4d861ef6e Support for TIM-B in GSE.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m32s
2025-11-10 18:18:39 +01:00
ft
f93d5e7f01 Documentation/sophia-net-signalling.md aktualisiert
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m14s
2025-11-10 11:52:03 +00:00
feyris-tan
6efec7f07b Can now parse SPT and TMST2 from GSE.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m45s
2025-11-09 22:47:22 +01:00
feyris-tan
a1125fbb2d Can now parse NIT, TDT, SCT, FCT2 and BCT from GSE.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m40s
2025-11-09 16:55:05 +01:00
feyris-tan
c64a0f5c46 Can now parse RMT, CMT and TBTP2 from GSE packets.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m29s
2025-11-09 12:16:44 +01:00
feyris-tan
8f2c31f10d Preparations for BFBS Navy TV.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m18s
2025-11-08 21:16:43 +01:00
feyris-tan
124bf9eb49 bfbs crc32
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m31s
2025-11-07 16:09:27 +01:00
feyris-tan
33d17a730b Sucessfulyl parsed our very first BFBS GSE Frame.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m0s
2025-11-07 14:03:27 +01:00
feyris-tan
fc8053451a Added some code coverage Tests for Docsis Annex C.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 3m0s
2025-11-07 13:21:38 +01:00
feyris-tan
5cd08b978d Added a "StreamlikeSpan"
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m15s
2025-11-06 21:42:51 +01:00
feyris-tan
5aa475dd46 Merge remote-tracking branch 'remotes/origin/ptsFrameDumper'
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m35s
2025-11-06 20:06:32 +01:00
feyris-tan
28d9b9b2f8 Merge remote-tracking branch 'remotes/origin/sauerland'
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m9s
# Conflicts:
#	skyscraper8/GS/GSE/GseReader.cs
#	skyscraper8/Properties/launchSettings.json
2025-11-06 20:02:37 +01:00
feyris-tan
f750f56e50 Two missing characters.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m32s
2025-11-06 19:57:33 +01:00
feyris-tan
781636e0a3 Added a further BFBS Test.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m39s
2025-11-06 16:04:55 +01:00
feyris-tan
980cec0332 Added sanity tests for BFBS BBFrames
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m46s
2025-11-06 15:34:56 +01:00
feyris-tan
5745cf0421 Detect HTTP traffic in Packages.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 2m31s
2025-11-04 16:36:00 +01:00
feyris-tan
c03d4238bf Added an option that dumps single BBFrames to files.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 2m21s
2025-11-04 16:21:01 +01:00
feyris-tan
e3b125e29d minor bug fixes.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m49s
2025-10-30 07:58:27 +01:00
feyris-tan
90051928eb The blindscan will not retry signals for a few times. 2025-10-26 10:04:22 +01:00
feyris-tan
c603b10096 Downgraded MinIO dependency. 6.0.5 has a known bug that breaks pretty much everything. 2025-10-24 21:23:59 +02:00
feyris-tan
c455dfa0ed Add Method stubs in the Data Storages
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m57s
2025-10-23 08:37:31 +02:00
feyris-tan
e8893f85ce Added support for LLDP in ULE's bridged frame mode.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m20s
2025-10-22 19:11:12 +02:00
feyris-tan
aa21725182 Enable/Disable Sub-TS dumping.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m21s
2025-10-21 18:10:57 +02:00
feyris-tan
897c5e95ca Dump packets from a SimminRadiomidun-styled stream.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m19s
2025-10-20 22:08:47 +02:00
feyris-tan
90f807343b Built a "sub-TS" handling system.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m26s
2025-10-19 23:05:23 +02:00
feyris-tan
4a90c8751f Added BCT parsing.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m19s
2025-10-18 22:04:52 +02:00
feyris-tan
2d8b66e1e8 Storage for FCT2.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m47s
2025-10-18 21:07:46 +02:00
feyris-tan
d69c4cda68 Implemented FCT2 and TBTP2.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m19s
2025-10-18 00:06:38 +02:00
feyris-tan
799f36dba8 The GS of Siminn Radiomidun can now get read.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m31s
2025-10-17 23:14:26 +02:00
feyris-tan
260821ed9d release 10
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m27s
2025-10-16 23:45:27 +02:00
ft
a340885f89 Merge pull request 'origin/master' (#3) from origin/master into master
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m25s
Reviewed-on: #3
2025-10-16 19:48:28 +00:00
feyris-tan
3342141539 Added a bit of skyscraper8 trivia.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m48s
2025-10-16 16:34:22 +02:00
feyris-tan
46798f4df1 Improved the DVB-NIP via GSE reader by a ton!
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m36s
2025-10-16 16:04:01 +02:00
feyris-tan
1e9b321bd7 BBframeDeencapsulator version 4 will hopefully save the day, and not break CI again.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m30s
2025-10-16 13:33:38 +02:00
feyris-tan
41d90d9360 Merge remote-tracking branch 'origin/bbframes-revisited'
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m17s
# Conflicts:
#	skyscraper8/GS/BbframeDeencapsulator2.cs
#	skyscraper8/GS/MisProcessor.cs
#	skyscraper8/GS/MisProcessors/0x00_Gse.cs
#	skyscraper8/GS/MisProcessors/UnknownStreamType.cs
#	skyscraper8/GS/Stid135TestingProgram.cs
#	skyscraper8/Program.cs
2025-10-16 08:54:10 +02:00
feyris-tan
6e35f26528 Reeimplementing GS/GSE...
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m32s
2025-10-15 16:05:50 +02:00
feyris-tan
396365fe72 Messing with BbframeDeencapsulator2
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m29s
2025-10-14 22:23:45 +02:00
feyris-tan
6bd2c81b64 Added an alternative implementation of the BbframeDeencapsulator for testing.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m52s
2025-10-14 20:09:37 +02:00
178 changed files with 7931 additions and 1643 deletions

View File

@ -33,7 +33,8 @@ jobs:
uses: akkuman/gitea-release-action@v1
with:
name: ${{ gitea.sha }}
md5sum: true
sha256sum: true
md5sum: false
sha256sum: false
tag_name: release_tag
files: |-
${{ gitea.workspace }}/skyscraper8-${{ gitea.sha }}.zip

3
.gitignore vendored
View File

@ -130,3 +130,6 @@ imgui.ini
/skyscraper8.Manual/skyscraper8.Manual.pdf
/skyscraper8.Manual/skyscraper8.Manual.synctex.gz
/.vs/skyscraper8/CopilotIndices/17.14.1231.31060
/.vs/skyscraper8/CopilotIndices/17.14.1290.42047
/Documentation/TSDuck-Samples/experiment2/*.ts
/.vs/skyscraper8/CopilotIndices/17.14.1431.25910

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Minio" Version="6.0.5" />
<PackageReference Include="Minio" Version="6.0.4" />
</ItemGroup>
<ItemGroup>

View File

@ -18,9 +18,13 @@ using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.Scraper.Storage.Utilities;
using skyscraper5.src.InteractionChannel.Model.Descriptors;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper5.src.Skyscraper.Scraper.Dns;
using skyscraper5.Teletext;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
using skyscraper8.InteractionChannel.Model2.Descriptors;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper5.Data.MySql
@ -259,6 +263,142 @@ namespace skyscraper5.Data.MySql
throw new NotImplementedException();
}
public bool TestForTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, byte frameFrameNumber)
{
throw new NotImplementedException();
}
public void StoreTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, Tbtp2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForFrameComposition2(ushort networkId, Fct2.Frame fct2)
{
throw new NotImplementedException();
}
public void InsertFct2Frame(ushort networkId, Fct2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForBroadcastConfiguration(ushort networkId, byte txTypeTxType)
{
throw new NotImplementedException();
}
public void InsertBroadcastConfiguration(ushort networkId, Bct.BroadcastConfiguration txType)
{
throw new NotImplementedException();
}
public bool TestForRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public void InsertRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
{
throw new NotImplementedException();
}
public bool TestForTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
{
throw new NotImplementedException();
}
public void InsertTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
{
throw new NotImplementedException();
}
public bool TestForTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
{
throw new NotImplementedException();
}
public void InsertTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
{
throw new NotImplementedException();
}
public bool TestForTimCorrectionMessageExtension(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimCorrectionMessageExtension(PhysicalAddress macAddress,
_0xb1_CorrectionMessageExtensionDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimControlAssignment(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimControlAssignment(PhysicalAddress macAddress, _0xa4_SyncAssignDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimSatelliteReturnLink(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimSatelliteReturnLink(PhysicalAddress macAddress, _0xa9_SatelliteReturnLinkDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForLowerLayerService(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimLowerLayerService(PhysicalAddress macAddress, _0xbb_LowerLayerServiceDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
{
throw new NotImplementedException();
}
public void InsertHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
{
throw new NotImplementedException();
}
public bool TestForTimLogonResponse(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimLogonResponse(PhysicalAddress macAddress, _0xb9_LogonResponseDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
{
throw new NotImplementedException();
}
public void InsertTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
{
throw new NotImplementedException();
}
public IEnumerable<Tuple<int, int, ProgramMapping>> SelectAllPmt()
{
throw new NotImplementedException();

View File

@ -2,13 +2,17 @@
using Npgsql;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model.Descriptors;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper5.src.Skyscraper.Scraper.Storage.Utilities;
using skyscraper8.InteractionChannel.Model2;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2.Descriptors;
namespace skyscraper5.Data.PostgreSql
{
@ -947,6 +951,112 @@ namespace skyscraper5.Data.PostgreSql
return false;
}
public bool TestForRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public void InsertRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
{
throw new NotImplementedException();
}
public bool TestForTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
{
throw new NotImplementedException();
}
public void InsertTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
{
throw new NotImplementedException();
}
public bool TestForTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
{
throw new NotImplementedException();
}
public void InsertTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
{
throw new NotImplementedException();
}
public bool TestForTimCorrectionMessageExtension(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimCorrectionMessageExtension(PhysicalAddress macAddress,
_0xb1_CorrectionMessageExtensionDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimControlAssignment(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimControlAssignment(PhysicalAddress macAddress, _0xa4_SyncAssignDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimSatelliteReturnLink(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimSatelliteReturnLink(PhysicalAddress macAddress, _0xa9_SatelliteReturnLinkDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForLowerLayerService(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimLowerLayerService(PhysicalAddress macAddress, _0xbb_LowerLayerServiceDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
{
throw new NotImplementedException();
}
public void InsertHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
{
throw new NotImplementedException();
}
public bool TestForTimLogonResponse(PhysicalAddress macAddress)
{
throw new NotImplementedException();
}
public void InsertTimLogonResponse(PhysicalAddress macAddress, _0xb9_LogonResponseDescriptor descriptor)
{
throw new NotImplementedException();
}
public bool TestForTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
{
throw new NotImplementedException();
}
public void InsertTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
{
throw new NotImplementedException();
}
private bool AreArraysEqual(byte[] l, byte[] r)
{
if (l.Length != r.Length)
@ -969,5 +1079,7 @@ namespace skyscraper5.Data.PostgreSql
command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)coordinate.Item1);
command.ExecuteNonQuery();
}
}
}

View File

@ -6,7 +6,9 @@ using NpgsqlTypes;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Dvb.TvAnytime;
using skyscraper5.Skyscraper.Headless;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.DvbNip;
using skyscraper8.InteractionChannel.Model2;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper5.Data.PostgreSql
@ -224,6 +226,36 @@ namespace skyscraper5.Data.PostgreSql
return result;
}
}
public bool TestForTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, byte frameFrameNumber)
{
throw new NotImplementedException();
}
public void StoreTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, Tbtp2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForFrameComposition2(ushort networkId, Fct2.Frame fct2)
{
throw new NotImplementedException();
}
public void InsertFct2Frame(ushort networkId, Fct2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForBroadcastConfiguration(ushort networkId, byte txTypeTxType)
{
throw new NotImplementedException();
}
public void InsertBroadcastConfiguration(ushort networkId, Bct.BroadcastConfiguration txType)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,22 @@
BITRATE=6000000
PCR_DISTANCE=6000
PCR_PID=1001
tsp -v -b $BITRATE \
-I null \
-P regulate --packet-burst 14 \
-P filter --every $PCR_DISTANCE --set-label 1 \
-P craft --only-label 1 --pid $PCR_PID --no-payload --pcr 0 \
-P continuity --pid $PCR_PID --fix \
-P pcradjust --pid $PCR_PID \
-P pat -c -a 1/1000 \
-P pmt -c -i 1 --pcr-pid $PCR_PID -p 1000 -a 1002/0x0d -a 1003/0x0d -a 1004/0x0d \
--add-stream-identifier --set-data-broadcast-id 1002/5 \
--set-data-broadcast-id 1003/5 --set-data-broadcast-id 1004/5 \
-P inject tdt.xml --pid 0x14 --bitrate 2000 --stuffing \
-P timeref --system-synchronous \
-P mpeinject -p 1002 -l 127.0.0.2 2000 --max-queue 8192 \
-P mpeinject -p 1003 -l 127.0.0.2 3000 --max-queue 8192 \
-P history \
-P until -s 60 \
-O file --max-size 100000000 experiment2-clean-60s.ts

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tsduck>
<TDT UTC_time="1984-01-01 00:13:37"/>
</tsduck>

View File

@ -7,12 +7,16 @@ the following Signalling applies:
|Stream Type|Interpretation |
|-----------|-----------------------------------|
|0x80 |TBS 6903-X GSE Packets |
|0x80 |STiD135 GS/GSE Packets |
|0x81 |AC-3 Audio |
|0x82 |PID only used for PCR |
|0x82 |PID only used for PCR, no payloads |
|0x83 |AC-3 True HD Audio |
|0x84 |AC-3+ Audio |
|0x85 |ULE (RFC 4326) |
|0x85 |DTS Audio |
|0x86 |SCTE 35 Splice Info |
|0x87 |ATSC Enhanced-AC-3 Audio |
|0x88 |ULE (RFC 4326) |
|0x91 |A52b/AC-3 Audio |
## Descriptors:

View File

@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<ItemGroup>

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

View File

@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<ItemGroup>
@ -27,4 +28,8 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,3 @@
{
"help":"https://go.microsoft.com/fwlink/?linkid=866610"
}

View File

@ -0,0 +1,7 @@
{
"profiles": {
"skyscraper8.UI.SDL2": {
"commandName": "Project"
}
}
}

View File

@ -588,11 +588,9 @@ namespace skyscraper5.Skyscraper.IO.CrazycatStreamReader
{
this.wrapped = client;
this.bufferedStream = new BufferedStream(wrapped.GetStream(), 96256);
this.packetsRemainBeforeStart = 512;
}
internal TcpClient wrapped;
internal BufferedStream bufferedStream;
private int packetsRemainBeforeStart;
public void Dispose()
{
@ -615,15 +613,6 @@ namespace skyscraper5.Skyscraper.IO.CrazycatStreamReader
internal void DropPacket(byte[] buffer, int offset, int length)
{
if (packetsRemainBeforeStart > 0)
{
if (packetsRemainBeforeStart == 512 || packetsRemainBeforeStart == 256 || packetsRemainBeforeStart == 128 || packetsRemainBeforeStart == 64 || packetsRemainBeforeStart == 32 || packetsRemainBeforeStart == 16 || packetsRemainBeforeStart == 8 || packetsRemainBeforeStart == 4 || packetsRemainBeforeStart == 2 || packetsRemainBeforeStart == 1)
{
Console.WriteLine("Waiting {0} packets before starting to send to client {1}...", packetsRemainBeforeStart, wrapped.Client.RemoteEndPoint);
}
packetsRemainBeforeStart--;
return;
}
bufferedStream?.Write(buffer, offset, length);
}
}

View File

@ -80,7 +80,15 @@ namespace skyscraper5.IO.StreamReader
public bool SetChannel(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw)
{
return UnsafeStreamReaderMethods.SetChannel(freq, symbrate, pol, fec, lof1, lof2, lofsw);
try
{
return UnsafeStreamReaderMethods.SetChannel(freq, symbrate, pol, fec, lof1, lof2, lofsw);
}
catch (DivideByZeroException dvze)
{
Console.WriteLine("Mitigated Division-by-Zero bug in StreamReader.dll.");
return false;
}
}
public bool SetChannelEx(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw, MOD_TYPE mod)

View File

@ -6,6 +6,7 @@
\usepackage{amssymb}
\usepackage{xcolor}
\usepackage{pifont,mdframed}
\usepackage{verbatim}
\usepackage{geometry}
@ -74,6 +75,7 @@ On Linux and macOS, any halfway recent shell should be fine.
.\skyscraper8.exe cscan tcp://127.0.0.1:6969
\end{verbatim}
\label{sec:gswarning}
\subsubsection{Working with GS using StreamReader}
There are a few caveats when working with StreamReader.dll to capture GS.
@ -94,7 +96,19 @@ Why is that? While StreamReader.dll's Deencapsulation works correctly in theory,
\subsection{Using a BDA compatible tuner}
Use TSDuck's \codeword{tsp} and use the \codeword{dvb} input plugin and the \codeword{ip} output plugin.
\\ TODO: Talk about TSDuck here.
Here's how to do it:
\begin{enumerate}
\item Run skyscraper8 from the commandline using the following command:
\begin{verbatim}
.\skyscraper8.exe udpin
\end{verbatim}
\item Then run \codeword{tsp} like this:
\begin{verbatim}
tsp -v -I dvb -a 4 --delivery-system DVB-S2 --frequency 11,523,000,000 \
--symbol-rate 22,000,000 --polarity horizontal -O ip 127.0.0.1:9003
\end{verbatim}
\end{enumerate}
\subsection{Using a video4linux compatible tuner}
See the paragraph about BDA tuners. The same applies to video4linux compatible tuners. TSDuck's \codeword{tsp} can be used on Windows and Linux in the same way.
@ -157,13 +171,15 @@ Extracted files will be written to the directory the command-line interpreter ha
If you are using Microsoft Windows, you can also DragnDrop a TS file onto the skyscraper8.exe. The extracted data will be placed into the same directory the TS is in.
\label{sec:cannotdo}
\section{What skyscraper8 can not do}
\begin{itemize}
\item Watch TV or Listen to Radio. Extracting files and playing back audio/video are two very different things, and skyscraper8 is not designed to do the latter. If this is something you're looking for, you probably want something like ProgDVB or DVBViewer.
\item Descramble encrypted channels. This is illegal in most, if not all, jurisdictions, and I want nothing to with that.
\item Circumvent copy protections. TS contained on Blu-Ray disc are usually scrambled using AACS. Although not too difficult, bypassing it is also a complicated legal matter, which is why I prefer not doing it.
\item Descramble encrypted DOCSIS\footnote{Data-Over-Cable Service Interface Specifications} Traffic. While skyscraper8 does understand DOCSIS traffic and is perfectly capable of sniffing packets from it using tuners like the TBS-6281 SE, it will drop any scrambled packets - meaning those that have the DOCSIS privacy extension enabled.
\item Do a professional grade analysis of a TS. There are many intricacies in broadcasting systems which are out of scope for a simple file extractor developed by a mere hobbyist. If you're looking for a professional grade analyzing tool, I'd recommend you to check out the tools included in VMA Video Analyzer, Dektec's StreamXpert, GkWare's StreamGuru, or the \codeword{analyze} plugin of TSDuck.
\item Do a professional grade analysis of a TS. There are many intricacies in broadcasting systems which are out of scope for a simple file extractor developed by a mere hobbyist. \hypertarget{bettertools}{If you're looking for a professional grade analyzing tool}, I'd recommend you to check out the tools included in VMA Video Analyzer, Dektec's StreamXpert, GkWare's StreamGuru, or the \codeword{analyze} plugin of TSDuck.
\item Handle MPEG-DASH streams. Currently, there are two known video formats used in DVB-NIP: HLS and MPEG-DASH. While skyscraper8 is able to extract segments of both types from a TS, I do not know of an easy way to playback MPEG-DASH formatted streams. HLS on the other hand is trivial.
\item Repair broken GS. When using a non STiD135-based tuner to tune to a GS, the framing gets broken in extremely weird ways. Also, when using StreamReader.dll to tune to a GS with a STiD135 - it is imperative to disable to StreamReader.dll's deencapsulator. It often breaks the framing as well. See \hyperref[sec:gswarning]{the notice about GS in Section 2.1}
\end{itemize}
@ -171,14 +187,79 @@ If you are using Microsoft Windows, you can also DragnDrop a TS file onto
\section{How skyscraper8 was made}
skyscraper8 is developed in the C\# programming language, using Microsoft Visual Studio 2022. Therefore it requires a Microsoft .NET runtime, which is included in Microsoft Windows and freely available for most Linux distributions and Apple's macOS.
The .NET assembly of skyscraper8 is not obfuscated or protected in any way. This is on purpose. If you want to study how skyscraper8 works under the hood, I hereby allow you to use tools like RedGate's Reflector or JetBrains' dotPeek to inspect the skyscraper8.dll file. Of course you can also acquire the plain source code from my Gitea instance.\footnote{\url{https://gitea.yo3explorer.moe/ft/skyscraper8}}
The .NET assembly of skyscraper8 is not obfuscated or protected in any way. This is on purpose. If you want to study how skyscraper8 works under the hood, I hereby allow you to use tools like RedGate's Reflector or JetBrains' dotPeek to inspect the skyscraper8.dll file. I plan to release the clear source code at a later date. \\
Although I can't and won't force you to give credit, if you find some of my source code inspiring or helpful and want to use it in your own projects, it would be great if you give credit to where you got it from. \\
This document was typeset in \LaTeX{}, using the TeX Live distribution, and while I wrote it myself, I did use GPT-5 for proofreading the initial version of this document. The proofreading of this document is the only part of skyscraper8 for which an LLM was used. No part of the actual skyscraper8 codebase itself was written by an LLM. \\
For generating artificial TS recordings to test on, TSDuck was used. \\
\subsection{Experiments conducted using TSDuck}
\subsubsection {Creating a TS that carried multiple sub-TS using MPE}
Since skyscraper8 public release 12, it's possible to have skyscraper8 extract one or more TS carried via Multiprotocol Encapsulation. Unfortunately the dish I have at home is not aimed at satellites carrying such a stream. So I used TSDuck to build one artificially. \\
The following script was used to to run \codeword{tsp}:
\verbatiminput{../Documentation/TSDuck-Samples/experiment2/run.sh}
Contents of tdt.xml:
\verbatiminput{../Documentation/TSDuck-Samples/experiment2/tdt.xml}
Before executing that script, run this beforehand, to get a stream of Stingray Spa, of course you can use any other IPTV station you'd like:
\begin{verbatim}
tsp -v -I hls https://lotus.stingray.com/manifest/ose-122ads-montreal/samsungtvplus/master.m3u8 \
-P regulate -P history -O ip 127.0.0.2:3000
\end{verbatim}
For the other stream, I used Offener Kanal Berlin, like so:
\begin{verbatim}
tsp -v -I hls https://alex-stream.rosebud-media.de/bounce/alexlivetv50.smil/index.m3u8 \
-P regulate -P history -O ip 127.0.0.2:2000
\end{verbatim}
Finally, run the script above to get a clean TS containing two other TS encapsulated in MPE.
This document was typeset in \LaTeX{}, using the TeX Live distribution, and while I wrote it myself, I did use GPT-5 for proofreading the initial version of this document. The proofreading of this document is the only part of skyscraper8 for which an LLM was used. No part of the actual skyscraper8 codebase itself was written by an LLM.
\subsection{Personal remarks and some useless bonus information}
\subsubsection{Credits}
skyscraper8 uses some external libraries:
\begin{itemize}
\item One of the external libraries is Ionic.Zlib.Core.dll, which is part of the \href{https://github.com/DinoChiesa/DotNetZip-2025}{DotNetZip} tool set by Dino Chiesa.
\item Also used is Newtonsoft.Json.dll a.k.a. \href{https://github.com/JamesNK/Newtonsoft.Json}{Json.NET} by James Newton-King.
\item Some of the GS handling code got inspired from the \href{https://github.com/newspaperman/bbframe-tools}{bbframe-tools} written by newspaperman.
\end{itemize}
\subsubsection{How and why skyscraper8 began: A young man's dream}
Ever since I was a child, I was fascinated by TV. But not actually watching it, rather understanding how it works. I grew up in the 90s, in an area where Cable TV was not really available, and terristial reception (yeah, those TVs with bunny ears!) only worked when it felt like it. So satellite was the best way to get my childish fix of cartoons! We can say the dish on my roof has always been a companion. Far more interesting than actually watching TV was scanning through the printed TV guides. I was curious. How did they make these? And how would I make one myself? I wondered. Of course my parents wouldn't know - they're not techies. \\
Of course, as I got older, I eventually moved onto other things. Like programming, trading cards, and video games. But just like with the TV, I eventually got more interested in how these games work, instead of actually playing them. \\
I got internet access much later in my life than my peers did, and when I found out that there are forums in which people discuss the technicalities about video games, it blew my mind. I used to be a frequent reader (and occasional contributor) of the XeNTaX\footnote{Unfortunately, that forum is long gone from the internet, but can still be experienced thanks to the amazing work of the people over at the Internet Archive: \url{https://web.archive.org/web/20230925120533/https://forum.xentax.com/ }} forum, from where I learned a lot! \\
At the time, my circle of friends was crazy about Yu-Gi-Oh! Online 3, which was an excellent simulation of the trading card game by the same name. I was really bad at this game, not even a mediocre player. I was far more busy studying how the game worked under the hood, and eventually managed to write a program that extracts all the card graphics and the soundtrack of the game. This resulted in my guild leader having the best quality available soundtrack rips on his Youtube account. As you can probably guess, I went on and reverse engineered more and more video games. But as life goes on, you eventually graduate from school, head to university, and then get a job. Interests and people can change over time, and so did I. As I grew older I kind of lost interest in video games. Nowadays I rarely play anymore. \\
But what has that to do with skyscraper8? That's easy! One day my parents, who I still stay in touch with asked me to record something off live TV. Up until that point, I totally forgot about the dish on the roof of the house my room is in, but at that point it got relevant again. Upon searching for an easy way to record satellite TV on a PC I stumbbled upon Thierry Lelegard's tsduck. With tsp's plugins, it was real easy to schedule a recording. And since I still had the natural curiosity of a game reverse engineer in me, I opened up the .ts file in a hex-editor, and that's exactly the point in time when the DX virus hit me. After reading a bit of the source code of \href{https://images.videolan.org/developers/libdvbpsi.html}{libdvbpsi}, an idea manifested in my head. And that idea became "skyscraper".
\subsubsection{A Brief history of skyscrapers1 to 8}
At that time had a job of administering some databases. My childhood fantasies of making my own TV guide were now within reach. I had "skyscraper" at home, a bunch of databases at work - and MySQL is free someware. That was shortly before COVID-19 ran wild in the world. During lockdown I had made it: A program that reads a list of frequencies from a database, record a series of TS files, parses all the DVB tables, and writes the results into the database. That was all the first skyscraper could do. It was written in Java, and missed a lot of things.
skyscraper5 came about when I changed jobs. I was a software developer at that time. And since we used Java at my day job, and I wanted some distance from my job, I switched my hobbyist projects back to .NET. - .NET 5 to be exact - that's why I called it skyscraper5. That was on the 5th of March, 2022.
Eventually, I upgraded to .NET 8, mainly due to dependency issues, and I renamed the project fittingly to skyscraper8. \\
If you read up to this point, I wish to thank you from the bottom of my heart. I guess by now you can tell that skyscraper8 is a very personal and dear matter for me. My goal was never to make the best tool for handling TS files, there are \hyperlink{bettertools}{much better options} regarding that, but to satisfy my desire to learn. And let me tell you, I learned a lot during the development of skyscraper8, both as a person and as a programmer.
\subsubsection{Music!}
Like a lot of programmers, I do enjoy listening to music while working. Some programmers even put song references in their software. Like how MKVToolnix' version names are actually song names, or how BSD developer fk even put \href{https://www.fabiankeil.de/nutzloseinfos.html}{a list of albums on his website} listing what albums he listened to while making it. Although this is absolutely useless, I'd liketo do this as well. Therefore, here follows a list of musical albums I listened to while developing skyscraper8 - no claim to completeness.
Like a lot of programmers, I do enjoy listening to music while working. Some programmers even put song references in their software. Like how MKVToolnix' version names are actually song names, or how BSD developer fk even put \href{https://www.fabiankeil.de/nutzloseinfos.html}{a list of albums on his website} listing what albums he listened to while making it. Although this is absolutely useless, I'd liketo do this as well. Therefore, here follows a list of musical albums I enjoyed listening to while developing skyscraper8 - no claim to completeness.
\begin{itemize}
\item Bel Canto - White-Out Conditions
@ -191,6 +272,8 @@ Like a lot of programmers, I do enjoy listening to music while working. Some pro
\item Satsuki Shibano / Yoshio Ojima - Caresse
\item SHIFT UP - Stellar Blade Original Soundtrack
\item Small Affairs - Small Affairs
\item Starmani Series - la fillette révolutionnaire UTENA
\item SuganoMusic - EUROBEAT FESTIVAL VOL.7.5
\item Yoshio Ojima - Hands-Some
\item Youssou NDour - The Guide (Wommat)
\item Yu-Gi-Oh! Sound Duel Quarter Century Collection

View File

@ -0,0 +1,5 @@
{
"allure": {
"directory": "allure-results"
}
}

View File

@ -1 +0,0 @@
[assembly: CollectionBehavior(DisableTestParallelization = true)]

View File

@ -1,102 +0,0 @@
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection.Contestants;
using skyscraper8.Ses;
using skyscraper8.Tests.ClassDependencies.AsraBarkerTransponderTests;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Tests
{
public class AstraBarkerTransponderTests
{
private const string BARKER_TRANSPONDER_FILE_PATH = "C:\\Temp\\Astra1_12604_v_SGT-000000.ts";
private Stream GetBarkerTransponder()
{
FileInfo fi = new FileInfo(BARKER_TRANSPONDER_FILE_PATH);
if (!fi.Exists)
{
Debug.WriteLine(String.Format("Could not find file: {0}", BARKER_TRANSPONDER_FILE_PATH));
return null;
}
return fi.OpenRead();
}
[Fact]
public void TestAstraSdt()
{
Stream stream = GetBarkerTransponder();
if (stream == null)
return;
SdtContestant contestant = new SdtContestant(0x002d);
TsDescriptorUnpacker descriptorUnpacker = TsDescriptorUnpacker.GetInstance();
for (byte i = 0x80; i < 0xfe; i++)
{
descriptorUnpacker.SetUserDefined(i, true);
}
descriptorUnpacker.SetUserDefined(0xfe, true);
TsContext tsContext = new TsContext();
tsContext.FilterChain = new List<skyscraper5.src.Mpeg2.PacketFilter.IPacketFilter>();
tsContext.FilterChain.Add(new DummyFilter());
tsContext.RegisterPacketProcessor(0x002d, contestant.PacketProcessor);
byte[] buffer = new byte[188];
while (stream.GetAvailableBytes() >= 188)
{
stream.Read(buffer, 0, 188);
tsContext.PushPacket(buffer);
if (contestant.Score >= 10)
break;
}
stream.Close();
}
[Fact]
public void TestAstraLcn()
{
Stream stream = GetBarkerTransponder();
if (stream == null)
return;
SgtCandidate contestant = new SgtCandidate(0x0777);
SgtCandidate contestant2 = new SgtCandidate(0x0776);
TsDescriptorUnpacker descriptorUnpacker = TsDescriptorUnpacker.GetInstance();
for (byte i = 0x80; i < 0xfe; i++)
{
descriptorUnpacker.SetUserDefined(i, true);
}
descriptorUnpacker.SetUserDefined(0xfe, true);
TsContext tsContext = new TsContext();
tsContext.FilterChain = new List<skyscraper5.src.Mpeg2.PacketFilter.IPacketFilter>();
tsContext.FilterChain.Add(new DummyFilter());
tsContext.RegisterPacketProcessor(0x0777, contestant.PacketProcessor);
tsContext.RegisterPacketProcessor(0x0776, contestant2.PacketProcessor);
byte[] buffer = new byte[188];
while (stream.GetAvailableBytes() >= 188)
{
stream.Read(buffer, 0, 188);
tsContext.PushPacket(buffer);
bool winnerA = contestant.Score >= 10;
bool winnerB = contestant2.Score >= 10;
if (winnerA)
{
if (winnerB)
{
return;
}
}
}
stream.Close();
}
}
}

View File

@ -1,18 +0,0 @@
using skyscraper5.Mpeg2;
using skyscraper5.src.Mpeg2.PacketFilter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Tests.ClassDependencies.AsraBarkerTransponderTests
{
internal class DummyFilter : IPacketFilter
{
public bool PassPacket(TsPacket packet)
{
return true;
}
}
}

View File

@ -1,40 +0,0 @@
using skyscraper5.Docsis;
using skyscraper5.Docsis.MacManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Tests.ClassDependencies.DocsisTests
{
internal class DocsisEventHandlerImpl : IDocsisEventHandler
{
public void OnCmtsTimestamp(PhysicalAddress source, uint timing)
{
throw new NotImplementedException();
}
public void OnDownstreamChannel(PhysicalAddress physicalAddress, MacDomainDescriptor.DownstreamActiveChannel downstreamActiveChannel)
{
throw new NotImplementedException();
}
public void OnLearnedIpFromMac(PhysicalAddress arpHeaderSenderHardwareAddress, IPAddress arpHeaderSenderProtocolAddress)
{
throw new NotImplementedException();
}
public void OnParticipantDetected(PhysicalAddress pa)
{
throw new NotImplementedException();
}
public void OnUpstreamChannel(UpstreamChannelDescriptor mmm)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,69 +0,0 @@
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5;
using skyscraper8.Skyscraper.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8.Tests
{
public class DocsisTestsLong
{
[Fact]
public void TestLongBoiSingleStreams()
{
DirectoryInfo di = new DirectoryInfo("D:\\DocsisDemo");
if (!di.Exists)
{
Debug.WriteLine("Files not available!");
return;
}
FilesystemScraperStorageFactory storageFactory = new FilesystemScraperStorageFactory();
storageFactory.Directory = "docsis_longboi";
Passing passing = new Passing();
passing.DataStorage = storageFactory.CreateDataStorage();
passing.MassImportDirectory(di);
}
[Fact]
public void TestM3u8Stream()
{
FileInfo docsisM3u8 = new FileInfo("D:\\DocsisDemo\\docsis.m3u8");
if (!docsisM3u8.Exists)
{
Debug.WriteLine("File not available!");
return;
}
FileInfo alreadyTested = new FileInfo("docsis_m3u8_test.complete");
if (alreadyTested.Exists)
{
Debug.WriteLine("Hello!");
return;
}
M3U8Stream m3u8 = new M3U8Stream(docsisM3u8.FullName);
TsContext tsContext = new TsContext();
InMemoryScraperStorageFactory storageFactory = new InMemoryScraperStorageFactory();
DataStorage scraperStorage = storageFactory.CreateDataStorage();
SkyscraperContext skyscraperContext = new SkyscraperContext(tsContext, scraperStorage, null);
skyscraperContext.InitalizeFilterChain();
skyscraperContext.IngestFromStream(m3u8);
File.WriteAllText("docsis_m3u8_test.complete", "1");
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Allure.Net.Commons;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestResult = Allure.Net.Commons.TestResult;
namespace skyscraper8.Tests;
[TestClass]
public class Feyllure
{
public TestContext TestContext { get; set; }
protected static readonly AllureLifecycle Allure = AllureLifecycle.Instance;
private bool assemblyWasInited;
private void AssemblyInit(TestContext context)
{
// Ensure allure-results exists
var resultsDir = Allure.ResultsDirectory;
Directory.CreateDirectory(resultsDir);
var envFile = Path.Combine(resultsDir, "environment.properties");
using (var writer = new StreamWriter(envFile))
{
writer.WriteLine($"SkyscraperRelease={VersionInfo.GetPublicReleaseNumber()}");
writer.WriteLine($"SkyscraperCodeVersion={VersionInfo.GetCurrentAssemblyDisplayVersion()}");
writer.WriteLine($"OS={RuntimeInformation.OSDescription}");
writer.WriteLine($".NET={Environment.Version}");
writer.WriteLine($"Machine={Environment.MachineName}");
writer.WriteLine($"User={Environment.UserName}");
writer.WriteLine($"Framework={System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
writer.WriteLine($"BuildDate={DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}");
}
var executorFile = Path.Combine(resultsDir, "executor.json");
var executorInfo = new
{
name = Environment.MachineName,
buildName = "My build name",
buildUrl = "http://127.0.0.2",
};
var executorJson = Newtonsoft.Json.JsonConvert.SerializeObject(executorInfo);
File.WriteAllText(executorFile, executorJson);
}
[TestInitialize]
public void Setup()
{
if (!assemblyWasInited){
AssemblyInit(TestContext);
assemblyWasInited = true;
}
var testName = TestContext.TestName;
var fqClass = TestContext.FullyQualifiedTestClassName;
// Extract namespace and class name
var lastDot = fqClass.LastIndexOf('.');
var ns = lastDot > 0 ? fqClass.Substring(0, lastDot) : "";
var cls = lastDot > 0 ? fqClass.Substring(lastDot + 1) : fqClass;
TestResult testResult = new TestResult();
testResult.uuid = Guid.NewGuid().ToString();
testResult.name = testName;
testResult.fullName = String.Format("{0}.{1}", TestContext.FullyQualifiedTestClassName, testName);
testResult.labels = new List<Label>()
{
//Packages Tab
new Label { name = "package", value = ns},
new Label { name = "testClass", value = cls},
new Label { name = "testMethod", value = testName},
//Suites tab
new Label { name = "parentSuite", value = ns},
new Label { name = "suite", value = cls},
};
Allure.StartTestCase(testResult);
descriptionWriter = null;
}
[TestCleanup]
public void TearDown()
{
switch (TestContext.CurrentTestOutcome)
{
case UnitTestOutcome.Passed:
Allure.UpdateTestCase(tc => tc.status = Status.passed);
break;
case UnitTestOutcome.Failed:
Allure.UpdateTestCase(tc => tc.status = Status.failed);
break;
default:
StepResult stepResult = new StepResult();
stepResult.name = String.Format("Current Test Outcome: {0}", TestContext.CurrentTestOutcome);;
Allure.UpdateTestCase(tc =>
{
tc.status = Status.skipped;
tc.steps.Add(stepResult);
});
break;
}
if (descriptionWriter != null)
{
Allure.UpdateTestCase(tc => { tc.description = descriptionWriter.ToString(); });
}
Allure.StopTestCase();
Allure.WriteTestCase();
}
private StringWriter descriptionWriter;
public void Print(string message, params object[] args)
{
if (descriptionWriter == null)
descriptionWriter = new StringWriter();
string formattedMessage = String.Format(message, args);
Console.WriteLine(formattedMessage);
descriptionWriter.WriteLine(formattedMessage);
}
}

View File

@ -59,105 +59,5 @@ namespace skyscraper8.Tests.Properties {
resourceCulture = value;
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] ModemCapabilitiesEncodingTest {
get {
object obj = ResourceManager.GetObject("ModemCapabilitiesEncodingTest", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] MultipartRegistrationResponseTest {
get {
object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] MultipartRegistrationResponseTest2 {
get {
object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] PushMacManagementMessage_Version4_Type45 {
get {
object obj = ResourceManager.GetObject("PushMacManagementMessage_Version4_Type45", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] ranging_response_test {
get {
object obj = ResourceManager.GetObject("ranging_response_test", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_1packet_01 {
get {
object obj = ResourceManager.GetObject("test-1packet-01", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_2packets_02_03 {
get {
object obj = ResourceManager.GetObject("test-2packets-02-03", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_3packets_04_05_06 {
get {
object obj = ResourceManager.GetObject("test-3packets-04-05-06", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] TransmitChannelConfigurationObject {
get {
object obj = ResourceManager.GetObject("TransmitChannelConfigurationObject", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] UpstreamChannelDescriptorTest {
get {
object obj = ResourceManager.GetObject("UpstreamChannelDescriptorTest", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -1,151 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ModemCapabilitiesEncodingTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ModemCapabilitiesEncodingTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="MultipartRegistrationResponseTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\T45_V4_MultipartRegistrationResponseTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="PushMacManagementMessage_Version4_Type45" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\PushMacManagementMessage_Version4_Type45.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ranging_response_test" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ranging_response_test.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="MultipartRegistrationResponseTest2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\T45_V4_MultipartRegistrationResponseTest2.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-1packet-01" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test-1packet-01.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-2packets-02-03" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test-2packets-02-03.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-3packets-04-05-06" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\test-3packets-04-05-06.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TransmitChannelConfigurationObject" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\TransmitChannelConfigurationObject.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UpstreamChannelDescriptorTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UpstreamChannelDescriptorTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,28 +0,0 @@
using skyscraper5.Docsis.MacManagement;
using System.Net.NetworkInformation;
using skyscraper8.Tests.Properties;
namespace skyscraper8.Tests
{
public class RangingResponseTest
{
[Fact]
public void OffsetBreakerTest()
{
byte[] blob = Resources.ranging_response_test;
Random rng = new Random();
byte[] sourceMacBuffer = new byte[6];
rng.NextBytes(sourceMacBuffer);
PhysicalAddress sourceAddress = new PhysicalAddress(sourceMacBuffer);
byte[] targetMacBuffer = new byte[6];
rng.NextBytes(targetMacBuffer);
PhysicalAddress targetAddress = new PhysicalAddress(targetMacBuffer);
RangingResponse rangingResponse = new RangingResponse(sourceAddress, targetAddress, blob);
Assert.True(rangingResponse.Valid);
}
}
}

View File

@ -1,38 +1,24 @@
using skyscraper5;
using System;
using skyscraper5.Docsis;
using skyscraper5.Docsis.AnnexC;
using skyscraper5.Docsis.MacManagement;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.Skyscraper.IO;
using skyscraper8.Tests.ClassDependencies.DocsisTests;
using skyscraper8.Tests.Properties;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using Xunit.Sdk;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace skyscraper8.Tests
namespace skyscraper8.Tests.ResourceTests
{
public class DocsisTests
[TestClass]
public class DocsisTests : Feyllure
{
[Fact]
[TestMethod]
public void ModemCapabilitiesEncoding()
{
byte[] buffer = Resources.ModemCapabilitiesEncodingTest;
byte[] buffer = Resources1.ModemCapabilitiesEncodingTest;
ModemCapabilitiesEncoding modemCapabilitiesEncoding = new ModemCapabilitiesEncoding(buffer);
Assert.True(modemCapabilitiesEncoding.Valid);
Assert.IsTrue(modemCapabilitiesEncoding.Valid);
}
[Fact]
[TestMethod]
public void MultipartRegistrationResponse()
{
Random rng = new Random();
@ -45,18 +31,18 @@ namespace skyscraper8.Tests
rng.NextBytes(targetBuffer);
PhysicalAddress target = new PhysicalAddress(targetBuffer);
byte[] buffer = Properties.Resources.MultipartRegistrationResponseTest;
byte[] buffer = Resources1.MultipartRegistrationResponseTest;
T45_V4_MultipartRegistrationResponse test = new T45_V4_MultipartRegistrationResponse(source, target, buffer);
Assert.True(test.Valid);
Assert.IsTrue(test.Valid);
}
[Fact]
[TestMethod]
public void MacManagement_4_45()
{
DocsisEventHandlerImpl docsisEventHandler = new DocsisEventHandlerImpl();
NullDocsisEventHandler docsisEventHandler = new NullDocsisEventHandler();
DocsisEnvironment environment = new DocsisEnvironment(docsisEventHandler);
byte[] testPayload = Resources.PushMacManagementMessage_Version4_Type45;
byte[] testPayload = Resources1.PushMacManagementMessage_Version4_Type45;
Random rng = new Random();
byte[] sourceBuffer = new byte[6];
@ -70,10 +56,10 @@ namespace skyscraper8.Tests
environment.PushMacManagementMessage(testPayload, 4, 45, source, target, testPayload);
}
[Fact]
[TestMethod]
public void InvalidUpstreamChannelDescriptorTest()
{
byte[] testPayload = Resources.UpstreamChannelDescriptorTest;
byte[] testPayload = Resources1.UpstreamChannelDescriptorTest;
Random rng = new Random();
byte[] sourceBuffer = new byte[6];
@ -85,19 +71,19 @@ namespace skyscraper8.Tests
PhysicalAddress target = new PhysicalAddress(targetBuffer);
UpstreamChannelDescriptor ucd = new UpstreamChannelDescriptor(source, target, testPayload);
Assert.False(ucd.Valid);
Assert.IsFalse(ucd.Valid);
}
[Fact]
[TestMethod]
public void TransmitChannelConfigurationObjectTest()
{
byte[] testPayload = Resources.TransmitChannelConfigurationObject;
byte[] testPayload = Resources1.TransmitChannelConfigurationObject;
CommonTlvEncodingObject.TransmitChannelConfigurationObject child = new CommonTlvEncodingObject.TransmitChannelConfigurationObject(testPayload);
Assert.True(child.Valid);
Assert.IsTrue(child.Valid);
}
[Fact]
[TestMethod]
public void MultipartRegistrationResponse2()
{
Random rng = new Random();
@ -110,9 +96,27 @@ namespace skyscraper8.Tests
rng.NextBytes(targetBuffer);
PhysicalAddress target = new PhysicalAddress(targetBuffer);
byte[] buffer = Properties.Resources.MultipartRegistrationResponseTest2;
byte[] buffer = Resources1.MultipartRegistrationResponseTest2;
T45_V4_MultipartRegistrationResponse test = new T45_V4_MultipartRegistrationResponse(source, target, buffer);
Assert.True(test.Valid);
Assert.IsTrue(test.Valid);
}
[TestMethod]
public void OffsetBreakerTest()
{
byte[] blob = Resources1.ranging_response_test;
Random rng = new Random();
byte[] sourceMacBuffer = new byte[6];
rng.NextBytes(sourceMacBuffer);
PhysicalAddress sourceAddress = new PhysicalAddress(sourceMacBuffer);
byte[] targetMacBuffer = new byte[6];
rng.NextBytes(targetMacBuffer);
PhysicalAddress targetAddress = new PhysicalAddress(targetMacBuffer);
RangingResponse rangingResponse = new RangingResponse(sourceAddress, targetAddress, blob);
Assert.IsTrue(rangingResponse.Valid);
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using skyscraper5.Mpeg2;
namespace skyscraper8.Tests.ResourceTests;
[TestClass]
public class GsType1SanityTest : Feyllure
{
[TestMethod]
public void CheckBfbsCrc()
{
byte[] blob = Resources1.Frame00001343_TSGS1_MIS000_SYNC001;
MemoryStream ms = new MemoryStream(blob, false);
bool result = DvbCrc32.ValidateCrc(ms, 0, (int)ms.Length);
Assert.IsTrue(result);
blob = Resources1.Frame00000008_TSGS1_MIS000_SYNC001;
ms = new MemoryStream(blob, false);
result = DvbCrc32.ValidateCrc(ms, 0, (int)ms.Length);
Assert.IsTrue(result);
}
[TestMethod]
public void CheckBfbsCrcSpan()
{
byte[] blob = Resources1.Frame00000012_TSGS1_MIS000_SYNC001;
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(blob);
bool result = DvbCrc32.ValidateCrc(span);
Assert.IsTrue(result);
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using skyscraper8.Ietf.Rfc4566_SDP;
namespace skyscraper8.Tests.ResourceTests
{
[TestClass]
public class SdpTest : Feyllure
{
[TestMethod]
public void TestSdpParser()
{
MemoryStream ms = new MemoryStream(Resources1.sdpTest, false);
Assert.IsTrue(SDP.IsSDP(ms));
SDP loaded = SDP.Load(ms);
Assert.IsNotNull(loaded);
}
}
}

View File

@ -1,28 +1,34 @@
using skyscraper5.Mpeg2;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.Tests.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8.Tests
namespace skyscraper8.Tests.ResourceTests
{
public class TsDuckTestPatterns
[TestClass]
public class SkyscraperTests : Feyllure
{
[Fact]
[TestMethod]
public void RunTestPatterns()
{
byte[][] buffers = new byte[][] { Resources.test_1packet_01, Resources.test_2packets_02_03, Resources.test_3packets_04_05_06 };
byte[][] buffers = new byte[][]
{
Resources1.test_1packet_01,
Resources1.test_2packets_02_03,
Resources1.test_3packets_04_05_06
};
InMemoryScraperStorageFactory imssf = new InMemoryScraperStorageFactory();
foreach (byte[] buffer in buffers)
{
TsContext mpeg2 = new TsContext();
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, null, null);
DataStorage ds = new InMemoryScraperStorageFactory().CreateDataStorage();
ObjectStorage os = new NullObjectStorage();
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, ds, os);
MemoryStream ms = new MemoryStream(buffer, false);
skyscraper.InitalizeFilterChain();

View File

@ -0,0 +1,10 @@
v=0
o=enensys Announcement-BM-SC 3962468565 IN IP4 10.10.10.20
s=Announcement delivery session
t=0 0
a=mbms-mode:broadcast-mbsfn 61712
a=source-filter: incl IN IP4 * 10.10.10.20
a=flute-tsi:9
m=application 50001 FLUTE/UDP 0
c=IN IP4 224.0.2.20/10
b=AS:200

203
skyscraper8.Tests/Resources1.Designer.cs generated Normal file
View File

@ -0,0 +1,203 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
namespace skyscraper8.Tests {
using System;
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources1 {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources1() {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("skyscraper8.Tests.Resources1", typeof(Resources1).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] Frame00000008_TSGS1_MIS000_SYNC001 {
get {
object obj = ResourceManager.GetObject("Frame00000008_TSGS1_MIS000_SYNC001", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] Frame00000012_TSGS1_MIS000_SYNC001 {
get {
object obj = ResourceManager.GetObject("Frame00000012_TSGS1_MIS000_SYNC001", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] Frame00001343_TSGS1_MIS000_SYNC001 {
get {
object obj = ResourceManager.GetObject("Frame00001343_TSGS1_MIS000_SYNC001", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] ModemCapabilitiesEncodingTest {
get {
object obj = ResourceManager.GetObject("ModemCapabilitiesEncodingTest", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] MultipartRegistrationResponseTest {
get {
object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] MultipartRegistrationResponseTest2 {
get {
object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] PushMacManagementMessage_Version4_Type45 {
get {
object obj = ResourceManager.GetObject("PushMacManagementMessage_Version4_Type45", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] ranging_response_test {
get {
object obj = ResourceManager.GetObject("ranging_response_test", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] sdpTest {
get {
object obj = ResourceManager.GetObject("sdpTest", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_1packet_01 {
get {
object obj = ResourceManager.GetObject("test-1packet-01", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_2packets_02_03 {
get {
object obj = ResourceManager.GetObject("test-2packets-02-03", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] test_3packets_04_05_06 {
get {
object obj = ResourceManager.GetObject("test-3packets-04-05-06", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] TransmitChannelConfigurationObject {
get {
object obj = ResourceManager.GetObject("TransmitChannelConfigurationObject", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] UpstreamChannelDescriptorTest {
get {
object obj = ResourceManager.GetObject("UpstreamChannelDescriptorTest", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ModemCapabilitiesEncodingTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\ModemCapabilitiesEncodingTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="MultipartRegistrationResponseTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\T45_V4_MultipartRegistrationResponseTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="PushMacManagementMessage_Version4_Type45" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\PushMacManagementMessage_Version4_Type45.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ranging_response_test" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\ranging_response_test.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="MultipartRegistrationResponseTest2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\T45_V4_MultipartRegistrationResponseTest2.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-1packet-01" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\test-1packet-01.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-2packets-02-03" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\test-2packets-02-03.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="test-3packets-04-05-06" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\test-3packets-04-05-06.ts;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TransmitChannelConfigurationObject" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\TransmitChannelConfigurationObject.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UpstreamChannelDescriptorTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\UpstreamChannelDescriptorTest.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Frame00001343_TSGS1_MIS000_SYNC001" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Frame00001343_TSGS1_MIS000_SYNC001.bbf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Frame00000008_TSGS1_MIS000_SYNC001" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Frame00000008_TSGS1_MIS000_SYNC001.bbf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="Frame00000012_TSGS1_MIS000_SYNC001" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Frame00000012_TSGS1_MIS000_SYNC001.bbf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="sdpTest" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\sdpTest.sdp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@ -0,0 +1,600 @@
using System;
using System.IO;
using System.Net.NetworkInformation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using skyscraper5.Docsis;
using skyscraper5.Docsis.AnnexC;
namespace skyscraper8.Tests.RootTests;
[TestClass]
public class DocsisTests : Feyllure
{
[TestMethod]
public void MacManagementMessageTypeAttributeTest()
{
MacManagementMessageTypeAttribute mmmta = new MacManagementMessageTypeAttribute(1, 2);
Print(mmmta.ToString());
}
[TestMethod]
public void RcpIdEncodingTest()
{
RcpIdEncoding rie = new RcpIdEncoding(new byte[] { 7, 1, 0 });
SimplifiedReceiveChannelAssignmentEncoding simplifiedReceiveChannelAssignmentEncoding = rie.SimplifiedReceiveChannelConfiguration;
Assert.IsNotNull(simplifiedReceiveChannelAssignmentEncoding);
Assert.Throws<NotImplementedException>(() => new RcpIdEncoding(new byte[] { 255, 1, 0 }));
}
[TestMethod]
public void EthernetLlcPacketClassificationEncodingTest()
{
EthernetLlcPacketClassificationEncoding epce = new EthernetLlcPacketClassificationEncoding(new byte[] { 3, 3, 1, 0, 1 });
ushort epceEProt = epce.EProt;
Assert.AreEqual(1, epce.EProt);
Assert.AreEqual(1, epce.Type);
Assert.Throws<NotImplementedException>(() => new EthernetLlcPacketClassificationEncoding(new byte[] { 255, 1, 0 }));
}
[TestMethod]
public void SimplifiedReceiveChannelAssignmentEncodingTest()
{
SimplifiedReceiveChannelAssignmentEncoding srcae =
new SimplifiedReceiveChannelAssignmentEncoding(new byte[] { 1, 1, 0, 2, 1, 0, 3, 1, 0 });
Assert.AreEqual(0,srcae.DownstreamChannelAssignment[0]);
Assert.AreEqual(0, srcae.DownstreamProfileAssignment[0]);
Assert.AreEqual(0, srcae.PrimaryDownstreamChannelAssignment[0]);
Assert.Throws<NotImplementedException>(() => new SimplifiedReceiveChannelAssignmentEncoding(new byte[] { 254,0,1 }));
}
[TestMethod]
public void TransmitChannelConfigurationObjectTest()
{
CommonTlvEncodingObject.TransmitChannelConfigurationObject tcco =
new CommonTlvEncodingObject.TransmitChannelConfigurationObject(
new byte[]
{
1, 1, 0,
2, 1, 0,
3, 1, 0,
5, 4, 0, 0, 0, 0,
6, 2, 0, 0,
7, 1, 1,
9, 1, 0,
10, 1, 0,
11, 1, 0
});
Assert.AreEqual(0, tcco.TccReferenceId.Value);
Assert.AreEqual(CommonTlvEncodingObject.TransmitChannelConfigurationObject.UpstreamChannelActionEnum.NoAction, tcco.UpstreamChannelAction.Value);
Assert.AreEqual(0, tcco.UpstreamChannelId.Value);
Assert.IsNotNull(tcco.UpstreamChannelDescriptor);
Assert.AreEqual(0, tcco.RangingSid.Value);
Assert.AreEqual(InitalizationTechniqueEnum.BroadcastInitialRanging, tcco.InitializationTechnique.Value);
Assert.AreEqual(0, tcco.DynamicRangeWindow);
Assert.AreEqual(0, tcco.P16Hi);
Assert.AreEqual(0, tcco.ListOfIucs[0]);
Assert.Throws<NotImplementedException>(() =>
new CommonTlvEncodingObject.TransmitChannelConfigurationObject(new byte[] { 254, 1, 0 }));
}
[TestMethod]
public void CommonTlvEncodingObjectTest()
{
MemoryStream ms = new MemoryStream(new byte[]
{
5, 1, 0,
47, 1, 0,
49, 1, 0,
31, 1, 0,
27, 1, 0,
30, 1, 0,
52, 2, 0, 1
});
CommonTlvEncodingObject commonTlv = new CommonTlvEncodingObject(ms);
Assert.IsNotNull(commonTlv.ModemCapabilitiesEncoding);
Assert.IsNotNull(commonTlv.ServiceFlowSidClusterAssignment);
Assert.IsNotNull(commonTlv.RcpId);
Assert.AreEqual(0, commonTlv.KeySequenceNumber);
Assert.AreEqual(0, commonTlv.HmacDigest[0]);
Assert.IsNotNull(commonTlv.AuthorizationHint);
Assert.AreEqual(new TimeSpan(0, 0, 1), commonTlv.InitializingChannelTimeout);
ms = new MemoryStream(new byte[] { 254, 1, 0 });
Assert.Throws<NotImplementedException>(() => new CommonTlvEncodingObject(ms));
}
[TestMethod]
public void GeneralPacketClassifierEncodingTest()
{
GeneralPacketClassifierEncoding gpce = new GeneralPacketClassifierEncoding(new byte[]
{
6, 1, 1,
5, 1, 0,
9, 1, 0,
3, 2, 0, 1,
1, 1, 0,
2, 2, 0, 1,
4, 4, 0, 0, 0, 1,
7, 1, 0,
10, 1, 0,
12, 16,
//IPv6 Packet Classification Encoding
3, 2, 0, 1,
4, 4, 127, 0, 0, 1,
6, 4, 127, 0, 0, 1
});
Assert.IsTrue(gpce.ClassifierActivationState.Value);
Assert.AreEqual(0, gpce.RulePriority.Value);
Assert.IsNotNull(gpce.Ipv4PacketClassification);
Assert.AreEqual(1, gpce.ServiceFlowReference.Value);
Assert.AreEqual(0, gpce.ClassifierReference.Value);
Assert.AreEqual(1, gpce.ClassifierIdentifier.Value);
Assert.AreEqual((uint)1, gpce.ServiceFlowIdentifier.Value);
Assert.AreEqual(GeneralPacketClassifierEncoding.DynamicServiceChangeActionEnum.Add, gpce.DynamicServiceChangeAction.Value);
Assert.IsNotNull(gpce.EthernetLlcPacketClassificationEncodings);
Assert.Throws<NotImplementedException>(() => new GeneralPacketClassifierEncoding(new byte[]
{
12, 4,
255, 1, 0, 0
}));
Assert.Throws<NotImplementedException>(() => new GeneralPacketClassifierEncoding(new byte[]
{
254, 1, 0
}));
}
[TestMethod]
public void Ipv4PacketClassificationEncodingTest()
{
GeneralPacketClassifierEncoding.Ipv4PacketClassificationEncodings ipce =
new GeneralPacketClassifierEncoding.Ipv4PacketClassificationEncodings(new byte[]
{
3, 4, 127, 0, 0, 1,
5, 4, 127, 0, 0, 1,
2, 2, 0, 1,
4, 4, 255, 255, 255, 0,
6, 4, 255, 255, 255, 0,
9, 2, 0, 1,
10, 2, 0, 1,
7, 2, 0, 1,
8, 2, 0, 1
});
Assert.IsNotNull(ipce.Ipv4SourceAddress);
Assert.IsNotNull(ipce.Ipv4DestinationAddress);
Assert.AreEqual(1, ipce.IpProtocol.Value);
Assert.IsNotNull(ipce.Ipv4SourceMask);
Assert.IsNotNull(ipce.Ipv4DestinationMask);
Assert.AreEqual(1, ipce.DestinationPortStart.Value);
Assert.AreEqual(1, ipce.DestinationPortEnd.Value);
Assert.AreEqual(1, ipce.SourcePortStart.Value);
Assert.AreEqual(1, ipce.SourcePortEnd.Value);
Assert.Throws<NotImplementedException>(() =>
new GeneralPacketClassifierEncoding.Ipv4PacketClassificationEncodings(new byte[]
{
255, 1, 0
}));
}
[TestMethod]
public void ServiceFlowSidClusterAssignmentObjectTest()
{
ServiceFlowSidClusterAssignmentObject sfscao = new ServiceFlowSidClusterAssignmentObject(new byte[]
{
1, 4, 0, 0, 0, 1,
2, 1, 0,
3, 1, 0,
});
Assert.AreEqual(1u, sfscao.SFID.Value);
Assert.IsNotNull(sfscao.SidClusterEncoding);
Assert.IsNotNull(sfscao.SidClusterSwitchoverCriteria);
Assert.Throws<NotImplementedException>(() => new ServiceFlowSidClusterAssignmentObject(new byte[]
{
255, 1, 0
}));
}
[TestMethod]
public void SidClusterEncodingObjectTest()
{
ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject sceo =
new ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject(new byte[]
{
1, 1, 1,
2, 1, 0
});
Assert.AreEqual(1, sceo.SidClusterId.Value);
Assert.IsNotNull(sceo.SidToChannelMapping);
Assert.Throws<NotImplementedException>(() => new ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject(new byte[]
{
255, 1, 0
}));
}
[TestMethod]
public void SidToChannelMappingObjectTest()
{
ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject.SidToChannelMappingObject stcmo =
new ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject.SidToChannelMappingObject(new byte[]
{
1,1,1,
2,2,0,1,
3,1,1
});
Assert.AreEqual(1, stcmo.UpstreamChannelId.Value);
Assert.AreEqual(1, stcmo.SID.Value);
Assert.AreEqual(ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject.SidToChannelMappingObject.ActionEnum.Add,stcmo.Action.Value);
Assert.Throws<NotImplementedException>(() =>
new ServiceFlowSidClusterAssignmentObject.SidClusterEncodingObject.SidToChannelMappingObject(new byte[]
{
255, 1, 0
}));
}
[TestMethod]
public void Ipv6PacketClassificationEncodingTest()
{
byte[] buffer = new byte[]
{
12, //Type Ipv6 Packet Classification Encoding
40, //Length of IPv6 Packet Classification Encoding
3,2, 0x08,0x00, //Next Header Type
4,16, 0xfd,0x58,0x2f,0x40,0xec,0xe9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
6,16, 0xfd,0x73,0x14,0x63,0x41,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
};
GeneralPacketClassifierEncoding gpce = new GeneralPacketClassifierEncoding(buffer);
Ipv6PacketClassificationEncoding ipce = gpce.Ipv6PacketClassification;
Assert.IsNotNull(ipce);
Assert.IsNotNull(ipce.Ipv6DestinationAddress);
Assert.IsNotNull(ipce.Ipv6SourceAddress);
Assert.AreEqual(0x0800, ipce.NextHeaderType);
}
[TestMethod]
public void SidClusterSwitchoverCriteriaObjectTest()
{
byte[] buffer = new byte[]
{
1, 1, 1,
2, 4, 0, 0, 0, 1,
3, 4, 0, 0, 0, 1,
4, 2, 0, 1
};
ServiceFlowSidClusterAssignmentObject.SidClusterSwitchoverCriteriaObject scsco =
new ServiceFlowSidClusterAssignmentObject.SidClusterSwitchoverCriteriaObject(buffer);
Assert.AreEqual((byte)1,scsco.MaximumRequestPerSidCluster);
Assert.AreEqual((uint)1, scsco.MaximumOutstandingBytesPerSidCluster);
Assert.AreEqual((uint)1, scsco.MaximumTotalBytesRequestedPerSidCluster);
Assert.AreEqual((ushort)1, scsco.MaximumTimeInTheSidCluster);
buffer = new byte[] { 254, 1, 0 };
Assert.Throws<NotImplementedException>(() =>
new ServiceFlowSidClusterAssignmentObject.SidClusterSwitchoverCriteriaObject(buffer));
}
[TestMethod]
public void ModemCapabilitiesEncodingTest()
{
byte[] buffer = new byte[]
{
1, 1, 1,
2, 1, 0,
3, 1, 1,
4, 1, 1,
6, 1, 1,
7, 1, 1,
8, 1, 1,
10, 1, 1,
11, 1, 1,
12, 1, 1,
15, 1, 1,
16, 4, 1, 2, 3, 4,
17, 1, 1,
18, 7, 1, 0, 0, 0, 0, 0, 0,
19, 1, 1,
20, 1, 2,
21, 1, 255,
22, 1, 1,
23, 1, 1,
24, 1, 2,
25, 1, 2,
26, 1, 2,
27, 1, 2,
28, 1, 2,
29, 1, 2,
30, 1, 3,
31, 1, 3,
32, 1, 3,
33, 1, 2,
34, 1, 1,
35, 1, 255,
36, 1, 3,
37, 1, 0,
38, 2, 0, 3,
39, 1, 1,
40, 1, 4,
44, 0,
46, 1, 1,
47, 4, 0, 0, 0, 255,
48, 2, 0, 4,
49, 1, 4,
50, 1, 5,
51, 1, 5,
52, 2, 1, 2,
53, 2, 1, 2,
54, 1, 1,
55, 1, 1,
56, 1, 0,
57, 1, 3,
58, 1, 5,
59, 2, 0, 5,
60, 1, 3,
76, 1, 7,
};
ModemCapabilitiesEncoding mcs = new ModemCapabilitiesEncoding(buffer);
Assert.IsTrue(mcs.ConcatenationSupport);
Assert.AreEqual(new Version(1, 0), mcs.DocsisVersion);
Assert.IsTrue(mcs.FragmentationSupport);
Assert.IsTrue(mcs.PayloadHeaderSuppressionSupport);
Assert.AreEqual(ModemCapabilitiesEncoding.PrivacySupportValue.BpiPlusSupport,mcs.PrivacySupport.Value);
Assert.AreEqual(1, mcs.DownstreamSaidSupport.Value);
Assert.AreEqual(1, mcs.UpstreamServiceFlowSupport.Value);
Assert.AreEqual(1, mcs.TransmitPreEqualizerTapsPerModulationInterval.Value);
Assert.AreEqual(1, mcs.NumberOfTransmitEqualizerTaps.Value);
Assert.IsTrue(mcs.DccSupport);
Assert.IsNotNull(mcs.RangingHoldOffSupport);
Assert.IsTrue(mcs.L2VPN);
Assert.IsNotNull(mcs.ESafeHostCapability);
Assert.IsTrue(mcs.DownstreamUnencryptedTrafficFiltering);
Assert.AreEqual(ModemCapabilitiesEncoding.UpstreamFrequencyRangeSupportEnum.Extended, mcs.UpstreamFrequencyRangeSupport);
Assert.IsTrue(mcs._160kspsSymbolRateSupport);
Assert.IsTrue(mcs._320kspsSymbolRateSupport);
Assert.IsTrue(mcs._640kspsSymbolRateSupport);
Assert.IsTrue(mcs._1280kspsSymbolRateSupport);
Assert.IsTrue(mcs._2560kspsSymbolRateSupport);
Assert.IsTrue(mcs._5120kspsSymbolRateSupport);
Assert.IsTrue(mcs.SelectableActiveCodeMode2Support);
Assert.IsTrue(mcs.CodeHoppingMode2Support);
Assert.AreEqual(2,mcs.ScQamMultipleTransmitChannelSupport.Value);
Assert.AreEqual(2, mcs.TotalSidClusterSupport.Value);
Assert.AreEqual(2, mcs.SidClustersPerServiceFlowSupport.Value);
Assert.AreEqual(2, mcs.ScQamMultipleReceiveChannelSupport.Value);
Assert.AreEqual(3, mcs.TotalDsidSupport.Value);
Assert.AreEqual(3, mcs.ResequencingDsidSupport.Value);
Assert.AreEqual(3, mcs.MulticastDsidSupport.Value);
Assert.AreEqual(ModemCapabilitiesEncoding.MulticastDsidForwardingEnum.SupportPromiscious, mcs.MulticastDsidForwarding.Value);
Assert.IsTrue(mcs.FrameControlTypeForwardingCapability);
Assert.IsTrue(mcs.DpvPerPathSuppported);
Assert.IsTrue(mcs.DpvPerPacketSuppported);
Assert.AreEqual(3, mcs.UnsolicitedGrantServiceFlowSupport.Value);
Assert.IsTrue(mcs.MapAndUcdReceiptSupport);
Assert.AreEqual(3, mcs.UpstreamDropClassifierSupport);
Assert.IsTrue(mcs.Ipv6Support.Value);
Assert.AreEqual(4, mcs.ExtendedUpstreamTransmitPowerCapability.Value);
Assert.IsTrue(mcs.CmStatusAck);
Assert.IsTrue(mcs.EnergyManagement1x1Feature);
Assert.IsTrue(mcs.DocsisLightSleepMode);
Assert.AreEqual(4, mcs.ExtendedPacketLengthSupportCapability);
Assert.AreEqual(4, mcs.OfdmMultipleReceiveChannelSupport.Value);
Assert.AreEqual(5, mcs.OfdmaMultipleTransmitChannelSupport.Value);
Assert.AreEqual(5, mcs.DownstreamOfdmProfileSupport.Value);
Assert.IsNotNull(mcs.DownstreamOfdmChannelSubcarrierQamModulationSupport);
Assert.IsNotNull(mcs.UpstreamOfdmaChannelSubcarrierQamModulationSupport);
Assert.AreEqual(108, mcs.DownstreamFrequencyRangeStarting);
Assert.AreEqual(1218, mcs.DownstreamFrequencyRangeUpTo);
Assert.AreEqual(42, mcs.UpstreamFrequencyRangeUpTo);
Assert.AreEqual(ModemCapabilitiesEncoding.DtpMode.MasterOrSlave, mcs.DocsisTimeProtocolMode.Value);
Assert.AreEqual(ModemCapabilitiesEncoding.DtpPerformanceSupport.Level5, mcs.DocsisTimeProtocolPerformanceSupport.Value);
Assert.AreEqual(5, mcs.Pmax);
Assert.IsTrue(mcs.DownstreamFullDuplex);
Assert.AreEqual(7, mcs.LowLatencySupport.Value);
buffer = new byte[]
{
2, 1, 1,
54, 1, 2,
55, 1, 2,
56, 1, 1,
};
mcs = new ModemCapabilitiesEncoding(buffer);
Assert.IsTrue(mcs._160kspsSymbolRateSupport);
Assert.IsTrue(mcs._320kspsSymbolRateSupport);
Assert.IsTrue(mcs._640kspsSymbolRateSupport);
Assert.IsTrue(mcs._1280kspsSymbolRateSupport);
Assert.IsTrue(mcs._2560kspsSymbolRateSupport);
Assert.IsTrue(mcs._5120kspsSymbolRateSupport);
buffer = new byte[]
{
2, 1, 2,
55, 1, 4,
56, 1, 2,
60, 1, 1,
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
2, 1, 3,
56, 1, 3,
60, 1, 2,
61, 1, 1
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
2, 1, 4,
56, 1, 4,
61, 1, 2
};
mcs = new ModemCapabilitiesEncoding(buffer);
Assert.IsNull(mcs.DpvPerPathSuppported);
Assert.IsNull(mcs.DpvPerPacketSuppported);
Assert.IsNull(mcs.EnergyManagement1x1Feature);
Assert.IsNull(mcs.DocsisLightSleepMode);
buffer = new byte[]
{
2, 1, 5,
61, 1, 4,
62, 1, 0
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
2, 1, 6,
62, 1, 1
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
62, 1, 2
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
62, 1, 3
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
62, 1, 4
};
mcs = new ModemCapabilitiesEncoding(buffer);
buffer = new byte[]
{
255, 1, 1
};
Assert.Throws<NotImplementedException>(() => new ModemCapabilitiesEncoding(buffer));
}
[TestMethod]
public void GeneralServiceFlowEncodingTest()
{
byte[] buffer = new byte[]
{
1, 2, 0, 1,
2, 2, 0, 1,
6, 1, 255,
7, 1, 1,
8, 4, 0, 0, 0, 1,
9, 4, 0, 0, 0, 1,
10, 4, 0, 0, 0, 1,
11, 2, 0, 1,
12, 2, 0, 1,
14, 2, 0, 1,
15, 1, 7,
17, 1, 1,
19, 2, 0, 1,
20, 4, 0, 0, 0, 2,
21, 4, 0, 0, 0, 2,
22, 1, 2,
23, 1, 2,
24, 4, 0, 0, 0, 2,
26, 1, 2,
32, 4, 0, 0, 0, 3,
16, 4, 0, 0, 255, 255,
};
GeneralServiceFlowEncoding gsfe = new GeneralServiceFlowEncoding(buffer);
Assert.AreEqual((ushort)1, gsfe.ServiceFlowReference);
Assert.AreEqual((ushort)1, gsfe.ServiceFlowIdentifier);
Assert.IsTrue(gsfe.ProvisionedSet);
Assert.IsTrue(gsfe.AdmittedSet);
Assert.IsTrue(gsfe.ActiveSet);
Assert.AreEqual((byte)1, gsfe.TrafficPriority);
Assert.AreEqual((uint)1, gsfe.UpstreamMaximumSustainedTrafficRate);
Assert.AreEqual((uint)1, gsfe.MaximumTrafficBurst);
Assert.AreEqual((uint)1, gsfe.MinimumReservedTrafficRate);
Assert.AreEqual((ushort)1, gsfe.AssumedMinimumReservedRatePacketSize);
Assert.AreEqual((ushort)1, gsfe.TimeoutForActiveQosParameters);
Assert.AreEqual(1, gsfe.MaximumConcatenatedBurst);
Assert.AreEqual(GeneralServiceFlowEncoding.ServiceFlowSchedulingTypeEnum.ProactiveGrantService, gsfe.ServiceFlowSchedulingType);
Assert.AreEqual(1, gsfe.DownstreamResequencing.Value);
Assert.AreEqual(1, gsfe.UnsolicitedGrantSize.Value);
Assert.AreEqual((uint)2, gsfe.NominalGrantInterval.Value);
Assert.AreEqual((uint)2, gsfe.ToleratedGrantJitter.Value);
Assert.AreEqual(2, gsfe.GrantsPerInterval.Value);
Assert.IsNotNull(gsfe.IpTypeOfServiceOverwrite);
Assert.AreEqual((uint)2, gsfe.CmtsTimestamp.Value);
Assert.AreEqual(2, gsfe.MultiplierToNumberOfBytesRequested);
Assert.AreEqual((uint)3, gsfe.ServiceFlowForbiddenAttributeMask);
Assert.IsTrue(gsfe.DoNotUseContentionRegions);
Assert.IsTrue(gsfe.DoNotUseSegmentHeaders);
Assert.IsTrue(gsfe.DropPacketsTooBigForUnsolicitedGrant);
Assert.IsTrue(gsfe.DoNotSupressPayloadHeaders);
Assert.IsTrue(gsfe.DoNotFragmentData);
Assert.IsTrue(gsfe.DoNotConcatenateData);
Assert.IsTrue(gsfe.DoNotPiggybackRequests);
Assert.IsTrue(gsfe.DoNotUseRequest2ForData);
Assert.IsTrue(gsfe.DoNotUseRequest2ForRequests);
Assert.IsTrue(gsfe.DoNotUsePriorityRequest);
Assert.IsTrue(gsfe.DoNotUseAllCms);
this.Print(gsfe.ToString());
buffer = new byte[]
{
17, 4, 0, 0, 0, 1
};
gsfe = new GeneralServiceFlowEncoding(buffer);
Assert.AreEqual((uint)1, gsfe.NominalPollingInterval.Value);
buffer = new byte[]
{
254, 1, 1
};
Assert.Throws<NotImplementedException>(() => new GeneralServiceFlowEncoding(buffer));
}
[MacManagementMessageType(0x90,0x01)]
class TestableMacManagementMessage : MacManagementMessage
{
public TestableMacManagementMessage(PhysicalAddress source, PhysicalAddress destination, byte[] buffer) : base(source, destination, buffer)
{
}
}
class UntestableMacManagementMessage : MacManagementMessage
{
public UntestableMacManagementMessage(PhysicalAddress source, PhysicalAddress destination, byte[] buffer) : base(source, destination, buffer)
{
}
}
[TestMethod]
public void MacManagementMessageTest()
{
Random rng = new Random();
byte[] srcBuffer = new byte[6];
byte[] dstBuffer = new byte[6];
PhysicalAddress srcAddr = new PhysicalAddress(srcBuffer);
PhysicalAddress dstAddr = new PhysicalAddress(dstBuffer);
byte[] dataBuffer = new byte[0];
MacManagementMessageTypeAttribute expected = new MacManagementMessageTypeAttribute(0x90, 0x01);
TestableMacManagementMessage tmmm = new TestableMacManagementMessage(srcAddr, dstAddr, dataBuffer);
Assert.AreEqual(expected, tmmm.MessageType);
Assert.AreEqual(srcAddr, tmmm.Source);
Assert.AreEqual(dstAddr, tmmm.Destination);
Assert.Throws<ArgumentNullException>(() => new TestableMacManagementMessage(srcAddr, dstAddr, null));
UntestableMacManagementMessage ummm = new UntestableMacManagementMessage(srcAddr, dstAddr, dataBuffer);
Assert.IsNull(ummm.MessageType);
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using skyscraper5.Mpeg2;
namespace skyscraper8.Tests.RootTests;
[TestClass]
public class Mpeg2Tests : Feyllure
{
[TestMethod]
public void PsiSectionTest()
{
PsiSection section = new PsiSection();
section.Append(new byte[] { 3 });
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Tests
{
public class SanityTests
{
[Fact]
public void Test15bits()
{
byte[] bcd = new byte[] { 0xa9, 0x23 };
MemoryStream ms = new MemoryStream(bcd);
byte readUInt8 = ms.ReadUInt8();
bool msbSet = (readUInt8 & 0x80) != 0;
Assert.True(msbSet);
int result = ms.ReadUInt8();
result <<= 8;
result += readUInt8;
Assert.Equal(9001, result);
}
}
}

View File

@ -1,42 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Target .NET 8 -->
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<!-- Mark this as a test project -->
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Xunit.SkippableFact" Version="1.5.23" />
<!-- MSTest framework -->
<PackageReference Include="Allure.Net.Commons" Version="2.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="4.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="4.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\skyscraper8\skyscraper8.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Resources1.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources1.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
@ -44,6 +38,10 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources1.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources1.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -51,8 +51,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper5.DNS", "MpePlugi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper5.Gimmicks.IptvCollector", "Gimmicks\skyscraper5.Gimmicks.IptvCollector\skyscraper5.Gimmicks.IptvCollector.csproj", "{8F17668C-623C-F9B3-EAD4-2922E5414B75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.Tests", "skyscraper8.Tests\skyscraper8.Tests.csproj", "{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UIs", "UIs", "{E23457C5-3A34-48EE-8107-C91E2C174B2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper5.UI.WindowsForms", "GUIs\skyscraper5.UI\skyscraper5.UI.WindowsForms.csproj", "{46CACA1C-F9B2-2FE0-2068-716F381325E9}"
@ -67,6 +65,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.MonoGame", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.MonoGame.Bridge", "GUIs\skyscraper8.UI.ImGui.MonoGame.Bridge\skyscraper8.UI.MonoGame.Bridge.csproj", "{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.Tests", "skyscraper8.Tests\skyscraper8.Tests.csproj", "{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -141,10 +141,6 @@ Global
{8F17668C-623C-F9B3-EAD4-2922E5414B75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F17668C-623C-F9B3-EAD4-2922E5414B75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F17668C-623C-F9B3-EAD4-2922E5414B75}.Release|Any CPU.Build.0 = Release|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Release|Any CPU.Build.0 = Release|Any CPU
{46CACA1C-F9B2-2FE0-2068-716F381325E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46CACA1C-F9B2-2FE0-2068-716F381325E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46CACA1C-F9B2-2FE0-2068-716F381325E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -169,6 +165,10 @@ Global
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A29F6E6-4B6A-DCCD-1DF1-AA8D020E17D2}.Release|Any CPU.Build.0 = Release|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84EE9FCD-2C7F-DF84-C1BA-99D018CE9412}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,4 +1,39 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAllureContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fc09614b59cc98c343d268f55e356aaafe85d13755cd5ca217988e9c5362ff_003FAllureContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAllureFeatureAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F405349a6f42433abaa8b1d6d06bb6f21c66899533fa9f9648cb1cde8755143_003FAllureFeatureAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F6e_003Fd247db11_003FArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssert_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F053cba69424c47858472194d509c3cdc12bb0_003F37_003F58cc70f4_003FAssert_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssert_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa840692e98b74557bc005b38213a22c72dad0_003Fc8_003F58c2e0c9_003FAssert_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADictionary_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F83_003Fc73c45bc_003FDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe1ab690537c44e02a014076312b886b7b2e200_003F5a_003Fcf76af61_003FFileInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInterop_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003Fea_003F7d70064b_003FInterop_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F6b_003Fa410ee2c_003FList_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemberInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003Fce_003F751a2c1f_003FMemberInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F0d_003F068af3a6_003FMemoryStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANullable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F0d_003F6549c49b_003FNullable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANullAsserts_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3f4719f7ce375226774d6991a6afc2f6932e58b42fd58ff13c7c03771642e_003FNullAsserts_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F55_003F6efc7017_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueue_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003Fb6_003F498e7c75_003FQueue_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeInformation_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F4c_003F5f771d10_003FRuntimeInformation_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003Fc6_003Fd8e0f2f2_003FSafeFileHandle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASkip_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fd8c543d93f1559af2ea2be8e9d55839b5bb1a3605f22daa45ea63772e3b4bc_003FSkip_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003Feb_003F3c476997_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe1ab690537c44e02a014076312b886b7b2e200_003F4f_003F7bfc5050_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/Profiling/Configurations/=1/@EntryIndexedValue">&lt;data&gt;&lt;HostParameters type="LocalHostParameters" /&gt;&lt;Argument type="StandaloneArgument"&gt;&lt;Arguments IsNull="False"&gt;&lt;/Arguments&gt;&lt;FileName IsNull="False"&gt;&lt;/FileName&gt;&lt;WorkingDirectory IsNull="False"&gt;&lt;/WorkingDirectory&gt;&lt;Scope&gt;&lt;ProcessFilters /&gt;&lt;/Scope&gt;&lt;/Argument&gt;&lt;Info type="TimelineInfo" /&gt;&lt;CoreOptions type="CoreOptions"&gt;&lt;CoreTempPath IsNull="False"&gt;&lt;/CoreTempPath&gt;&lt;RemoteEndPoint IsNull="False"&gt;&lt;/RemoteEndPoint&gt;&lt;AdditionalEnvironmentVariables /&gt;&lt;/CoreOptions&gt;&lt;HostOptions type="HostOptions"&gt;&lt;HostTempPath IsNull="False"&gt;&lt;/HostTempPath&gt;&lt;/HostOptions&gt;&lt;/data&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffb63d7b4f026464dbf9b2db60c7f76bc2ac00_003Fbd_003F4080deac_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATuple_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F57d616db882b441b8c50720b4477e03db2e200_003F9f_003F0d16f921_003FTuple_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;
&lt;Assembly Path="/home/schiemas/.dotnet/packs/Microsoft.NETCore.App.Ref/8.0.21/ref/net8.0/System.Windows.dll" /&gt;
&lt;Assembly Path="/home/schiemas/.nuget/packages/allure.net.commons/2.14.1/lib/netstandard2.0/Allure.Net.Commons.dll" /&gt;
&lt;/AssemblyExplorer&gt;</s:String>
<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">/home/schiemas/.cache/JetBrains/Rider2025.1/resharper-host/temp/Rider/vAny/CoverageData/_skyscraper8.1808907683/Snapshot/snapshot.utdcvr</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=92c8346e_002D5416_002D4320_002Dab1d_002D051307b205cd/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" Name="All tests from &amp;lt;skyscraper8.Tests&amp;gt; #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Project Location="\home\schiemas\RiderProjects\skyscraper8\skyscraper8.Tests" Presentation="&amp;lt;skyscraper8.Tests&amp;gt;" /&gt;&#xD;
&lt;/SessionState&gt;</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=94eea68c_002Dcaa0_002D4657_002Da521_002D7b96c8ead0ec/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &amp;lt;skyscraper8.Tests&amp;gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Project Location="\home\schiemas\RiderProjects\skyscraper8\skyscraper8.Tests" Presentation="&amp;lt;skyscraper8.Tests&amp;gt;" /&gt;&#xD;
&lt;/SessionState&gt;</s:String>
<s:String x:Key="/Default/Profiling/Configurations/=1/@EntryIndexedValue">&lt;data&gt;&lt;HostParameters type="LocalHostParameters" /&gt;&lt;Argument type="StandaloneArgument"&gt;&lt;Arguments IsNull="False"&gt;&lt;/Arguments&gt;&lt;FileName IsNull="False"&gt;&lt;/FileName&gt;&lt;WorkingDirectory IsNull="False"&gt;&lt;/WorkingDirectory&gt;&lt;Scope&gt;&lt;ProcessFilters /&gt;&lt;/Scope&gt;&lt;/Argument&gt;&lt;Info type="TimelineInfo" /&gt;&lt;CoreOptions type="CoreOptions"&gt;&lt;CoreTempPath IsNull="False"&gt;&lt;/CoreTempPath&gt;&lt;RemoteEndPoint IsNull="False"&gt;&lt;/RemoteEndPoint&gt;&lt;AdditionalEnvironmentVariables /&gt;&lt;/CoreOptions&gt;&lt;HostOptions type="HostOptions"&gt;&lt;HostTempPath IsNull="False"&gt;&lt;/HostTempPath&gt;&lt;/HostOptions&gt;&lt;/data&gt;</s:String>
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=skyscraper8_002ETests_002FResources1/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,15 @@
[
{
"Name": "Neues Profil",
"Projects": [
{
"Path": "IoPlugins\\skyscraper5.IO.StreamReader.RemoteStreamReaderServer\\skyscraper5.IO.StreamReader.RemoteStreamReaderServer.csproj",
"Action": "Start"
},
{
"Path": "GUIs\\skyscraper8.UI.ImGui\\skyscraper8.UI.SDL2.csproj",
"Action": "Start"
}
]
}
]

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Abertis
{
internal class AbertisSubSkyscraperKey
{
public int Pid { get; }
public AbertisSubSkyscraperKey(int pid)
{
Pid = pid;
}
public override string ToString()
{
return String.Format("{0:X4}", Pid);
}
}
}

View File

@ -11,7 +11,7 @@ namespace skyscraper5.Docsis.AnnexC
public EthernetLlcPacketClassificationEncoding(byte[] buffer)
{
MemoryStream ms = new MemoryStream(buffer, false);
while (ms.GetAvailableBytes() > 3)
while (ms.GetAvailableBytes() >= 3)
{
byte type = ms.ReadUInt8();
ushort length = ms.ReadUInt8();

View File

@ -149,6 +149,6 @@ namespace skyscraper5.Docsis.AnnexC
public uint? ServiceFlowIdentifier { get; set; }
public EthernetLlcPacketClassificationEncoding EthernetLlcPacketClassificationEncodings { get; }
internal Ipv6PacketClassificationEncoding Ipv6PacketClassification { get; }
public Ipv6PacketClassificationEncoding Ipv6PacketClassification { get; }
}
}

View File

@ -3,7 +3,7 @@ using System.Net;
namespace skyscraper5.Docsis.AnnexC
{
internal class Ipv6PacketClassificationEncoding
public class Ipv6PacketClassificationEncoding
{
public Ipv6PacketClassificationEncoding(byte[] buffer)
{

View File

@ -12,7 +12,7 @@ namespace skyscraper5.Docsis.AnnexC
{
private readonly bool? ExpandedUnicastSidSpace;
private readonly byte? DpvCapability;
private readonly byte EnergyManagementPreference;
private readonly byte? EnergyManagementPreference;
private byte? UpstreamScQamSymbolRateSupport;
private byte? _512MspsUpstreamTransmitScQamChannelSupport;
private byte? _256MspsUpstreamTransmitScQamChannelSupport;
@ -207,7 +207,7 @@ namespace skyscraper5.Docsis.AnnexC
DownstreamFrequencyRangeStarting = 108;
else if (v[0] == 0x02)
DownstreamFrequencyRangeStarting = 258;
else if (v[0] == 0x003)
else if (v[0] == 0x03)
DownstreamFullDuplex = true;
break;
case 61:
@ -235,7 +235,7 @@ namespace skyscraper5.Docsis.AnnexC
break;
default:
//CM-SP-MULPIv4.0-I01-190815.pdf, page 688
throw new NotFiniteNumberException(String.Format("{0} {1}", nameof(ModemCapabilitiesEncoding), type));
throw new NotImplementedException(String.Format("{0} {1}", nameof(ModemCapabilitiesEncoding), type));
}
}
Valid = true;
@ -268,7 +268,7 @@ namespace skyscraper5.Docsis.AnnexC
public byte? TransmitPreEqualizerTapsPerModulationInterval { get; }
public byte? NumberOfTransmitEqualizerTaps { get; }
public bool? DccSupport { get; }
internal RangingHoldOffSupport RangingHoldOffSupport { get; }
public RangingHoldOffSupport RangingHoldOffSupport { get; }
public bool? L2VPN { get; }
public ESafeHostCapability ESafeHostCapability { get; }
public bool? DownstreamUnencryptedTrafficFiltering { get; }
@ -446,5 +446,5 @@ namespace skyscraper5.Docsis.AnnexC
Level5 = 5,
NoSpecifiedPerformance = 6,
}
}
}
}

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace skyscraper8.Docsis.AnnexC
{
internal class RangingHoldOffSupport
public class RangingHoldOffSupport
{
public RangingHoldOffSupport(byte a, byte b, byte c, byte d)
{

View File

@ -27,7 +27,7 @@ namespace skyscraper5.Docsis.AnnexC
SimplifiedReceiveChannelConfiguration = new SimplifiedReceiveChannelAssignmentEncoding(v);
break;
default:
throw new NotFiniteNumberException(String.Format("{0} {1}", nameof(ModemCapabilitiesEncoding), type));
throw new NotImplementedException(String.Format("{0} {1}", nameof(ModemCapabilitiesEncoding), type));
}
}
}
@ -59,7 +59,7 @@ namespace skyscraper5.Docsis.AnnexC
break;
default:
//page 728, CM-SP-MULPIv4.0-I01-190815.pdf
throw new NotFiniteNumberException(String.Format("{0} {1}", nameof(SimplifiedReceiveChannelAssignmentEncoding), type));
throw new NotImplementedException(String.Format("{0} {1}", nameof(SimplifiedReceiveChannelAssignmentEncoding), type));
}
}
}

View File

@ -30,8 +30,6 @@ namespace skyscraper5.Docsis
{
Type type = this.GetType();
object[] customAttributes = type.GetCustomAttributes(typeof(MacManagementMessageTypeAttribute), false);
if (customAttributes == null)
return null;
if (customAttributes.Length == 0)
return null;
MacManagementMessageTypeAttribute attribute = (MacManagementMessageTypeAttribute)customAttributes[0];

View File

@ -0,0 +1,33 @@
using System.Net;
using System.Net.NetworkInformation;
using skyscraper5.Docsis.MacManagement;
namespace skyscraper5.Docsis;
public class NullDocsisEventHandler : IDocsisEventHandler
{
public void OnParticipantDetected(PhysicalAddress pa)
{
throw new NotImplementedException();
}
public void OnCmtsTimestamp(PhysicalAddress source, uint timing)
{
throw new NotImplementedException();
}
public void OnUpstreamChannel(UpstreamChannelDescriptor mmm)
{
throw new NotImplementedException();
}
public void OnDownstreamChannel(PhysicalAddress physicalAddress, MacDomainDescriptor.DownstreamActiveChannel downstreamActiveChannel)
{
throw new NotImplementedException();
}
public void OnLearnedIpFromMac(PhysicalAddress arpHeaderSenderHardwareAddress, IPAddress arpHeaderSenderProtocolAddress)
{
throw new NotImplementedException();
}
}

View File

@ -7,7 +7,7 @@ using skyscraper5.Ietf.Rfc971;
namespace skyscraper5.Dvb.DataBroadcasting
{
interface IMultiprotocolEncapsulationEventHandler
public interface IMultiprotocolEncapsulationEventHandler
{
void OnIpv4PacketArrival(InternetHeader internetHeader, byte[] ipv4Packet);
void OnIpDatagram(int sourcePid, byte[] payload);

View File

@ -17,6 +17,7 @@ namespace skyscraper5.Dvb.Descriptors
public NetworkNameDescriptor(byte[] buffer)
{
NetworkName = En300468AnnexATextDecoder.GetInstance().Decode(buffer);
Valid = true;
}
public string NetworkName { get; private set; }

View File

@ -50,6 +50,8 @@ namespace skyscraper5.Dvb.Descriptors
break;
}
}
Valid = true;
}
public int ModulationType { get; private set; }
@ -100,5 +102,20 @@ namespace skyscraper5.Dvb.Descriptors
NineOfTen = 9,
NoConventionalCoding = 15
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(this.OrbitalPosition);
sb.Append("°");
sb.Append(this.East ? 'E' : 'W');
sb.Append("/");
sb.Append(Frequency / 100);
sb.Append("/");
sb.Append(this.Polarization.ToString()[0]);
sb.Append("/");
sb.Append(this.SymbolRate / 10);
return sb.ToString();
}
}
}

View File

@ -351,5 +351,10 @@ namespace skyscraper5.Dvb.Descriptors
{
return HashCode.Combine((int)LinkageType, ServiceId, OriginalNetworkId, TransportStreamId);
}
public override string ToString()
{
return String.Format("{0} on (0x{1:X4},0x{2:X4},0x{3:X4})", LinkageType, OriginalNetworkId, TransportStreamId, ServiceId);
}
}
}

View File

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using moe.yo3explorer.skyscraper8.DVBI.Model;
using skyscraper8.DvbI;
using System.Reflection.Metadata.Ecma335;
using skyscraper8.Ietf.Rfc4566_SDP;
namespace skyscraper8.DvbNip
{
@ -53,16 +54,28 @@ namespace skyscraper8.DvbNip
if (discoveryUdpPacket.DestinationPort == 3937)
{
LctFrame discoveryLctFrame = new LctFrame(discoveryUdpPacket.Payload);
if (discoveryLctFrame.LctHeader.NipActualCarrierInformation != null)
if (discoveryLctFrame == null)
return;
LctHeader lctHeader = discoveryLctFrame.LctHeader;
if (lctHeader == null)
return;
if (lctHeader.NipActualCarrierInformation != null)
{
CurrentCarrierInformation = discoveryLctFrame.LctHeader.NipActualCarrierInformation;
EventHandler.OnNipCarrierDetected(CurrentCarrierInformation);
bootstrapped = true;
}
else if (discoveryLctFrame.PayloadContainsNativeIpMulticastTransportObject)
{
CurrentCarrierInformation = new NipActualCarrierInformation(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, "(no NIP carrier name provided)");
EventHandler.OnNipCarrierDetected(CurrentCarrierInformation);
bootstrapped = true;
}
}
}
return;
}
UserDatagram udpPacket = new UserDatagram(ipv4Packet);
LctFrame lctFrame = new LctFrame(udpPacket.Payload);
if (lctFrame.LctHeader == null)
@ -114,6 +127,10 @@ namespace skyscraper8.DvbNip
SetFileAssociations(fluteListener, fdtAnnouncement);
}
}
else
{
Console.WriteLine("aaa");
}
fluteStream.Close();
fluteStream.Dispose();
flutes.Remove(fluteCoordinate);
@ -164,62 +181,90 @@ namespace skyscraper8.DvbNip
}
}
private DateTime? currentTime;
private bool HandleMetadata(FluteListener fluteListener)
{
switch (fluteListener.FileAssociation.ContentLocation)
{
case "urn:dvb:metadata:cs:NativeIPMulticastTransportObjectTypeCS:2023:bootstrap":
MulticastGatewayConfigurationType multicastGatewayConfiguration2023 = DvbNipUtilities.UnpackMulticastGatewayConfiguration(fluteListener.ToStream());
EventHandler?.OnMulticastGatewayConfiguration(CurrentCarrierInformation, multicastGatewayConfiguration2023);
return true;
case "urn:dvb:metadata:cs:MulticastTransportObjectTypeCS:2021:gateway-configuration":
MulticastGatewayConfigurationType multicastGatewayConfiguration2021 = DvbNipUtilities.UnpackMulticastGatewayConfiguration(fluteListener.ToStream());
EventHandler?.OnMulticastGatewayConfiguration(CurrentCarrierInformation, multicastGatewayConfiguration2021);
return true;
case "urn:dvb:metadata:nativeip:PrivateDataSignalling":
PrivateDataSignallingManifestType privateDataSignallingManifest = DvbNipUtilities.UnpackPrivateDataSignallingManifest(fluteListener.ToStream());
EventHandler?.OnPrivateDataSignallingManifest(CurrentCarrierInformation, privateDataSignallingManifest);
return true;
case "urn:dvb:metadata:nativeip:dvb-i-slep":
if (currentTime.HasValue)
Stream stream = fluteListener.ToStream();
if (DvbNipUtilities.IsXml(stream))
{
Stream rawSlepStream = fluteListener.ToStream();
byte[] rawSlepBytes = new byte[rawSlepStream.Length];
rawSlepStream.Read(rawSlepBytes, 0, (int)rawSlepStream.Length);
string rawSlepString = Encoding.UTF8.GetString(rawSlepBytes);
rawSlepString = rawSlepString.Replace("<dvbi-types:", "<");
rawSlepString = rawSlepString.Replace("</dvbi-types:", "</");
rawSlepString = rawSlepString.Replace("<dvbisdt:", "<");
rawSlepString = rawSlepString.Replace("</dvbisdt:", "</");
ServiceListEntryPoints serviceListEntryPoints = DvbIUtils.UnpackServiceListEntryPoints(rawSlepString);
EventHandler?.OnServiceListEntryPoints(CurrentCarrierInformation, serviceListEntryPoints, currentTime.Value, this);
MulticastGatewayConfigurationType multicastGatewayConfiguration2023 = DvbNipUtilities.UnpackMulticastGatewayConfiguration(stream);
EventHandler?.OnMulticastGatewayConfiguration(CurrentCarrierInformation, multicastGatewayConfiguration2023);
return true;
}
else if (DvbNipUtilities.IsMime(stream))
{
stream = DvbNipUtilities.GetFirstMimeBodyPart(stream);
if (SDP.IsSDP(stream))
{
SDP loadedSdp = SDP.Load(stream);
MulticastGatewayConfigurationType convertedSdp = loadedSdp.ToMulticastGatewayConfiguration();
EventHandler?.OnMulticastGatewayConfiguration(CurrentCarrierInformation, convertedSdp);
return true;
}
else
{
stream.DumpToFile("container2.bin");
logger.WarnFormat("Failed to detect the Metadata format of the Native IP Multicast Transport Object.");
return false;
}
}
else
{
logger.WarnFormat("Failed to detect the Metadata format of the Native IP Multicast Transport Object.");
return false;
}
break;
case "urn:dvb:metadata:nativeip:TimeOffsetFile":
TimeOffsetFileType timeOffsetFile = DvbNipUtilities.UnpackTimeOffsetFile(fluteListener.ToStream());
EventHandler?.OnTimeOffsetFile(CurrentCarrierInformation, timeOffsetFile);
return true;
case "urn:dvb:metadata:nativeip:NetworkInformationFile":
NetworkInformationFileType networkInformationFile = DvbNipUtilities.UnpackNetworkInformationFile(fluteListener.ToStream());
EventHandler?.OnNetworkInformationFile(CurrentCarrierInformation, networkInformationFile);
return true;
case "urn:dvb:metadata:nativeip:ServiceInformationFile":
ServiceInformationFileType serviceInformationFile = DvbNipUtilities.UnpackServiceInformationFile(fluteListener.ToStream());
EventHandler?.OnServiceInformationFile(CurrentCarrierInformation, serviceInformationFile);
return true;
case "urn:dvb:metadata:nativeip:ServiceGuide":
//Unfortunately, the NIPServiceGuideManifest does not contain any useful information at all, which is why we ignore it.
//There doesn't seem to be a way to get the actual EIT XML from it.
return true;
default:
throw new NotImplementedException(fluteListener.FileAssociation.ContentLocation);
}
case "urn:dvb:metadata:cs:MulticastTransportObjectTypeCS:2021:gateway-configuration":
MulticastGatewayConfigurationType multicastGatewayConfiguration2021 = DvbNipUtilities.UnpackMulticastGatewayConfiguration(fluteListener.ToStream());
EventHandler?.OnMulticastGatewayConfiguration(CurrentCarrierInformation, multicastGatewayConfiguration2021);
return true;
case "urn:dvb:metadata:nativeip:PrivateDataSignalling":
PrivateDataSignallingManifestType privateDataSignallingManifest = DvbNipUtilities.UnpackPrivateDataSignallingManifest(fluteListener.ToStream());
EventHandler?.OnPrivateDataSignallingManifest(CurrentCarrierInformation, privateDataSignallingManifest);
return true;
case "urn:dvb:metadata:nativeip:dvb-i-slep":
if (currentTime.HasValue)
{
Stream rawSlepStream = fluteListener.ToStream();
byte[] rawSlepBytes = new byte[rawSlepStream.Length];
rawSlepStream.Read(rawSlepBytes, 0, (int)rawSlepStream.Length);
string rawSlepString = Encoding.UTF8.GetString(rawSlepBytes);
rawSlepString = rawSlepString.Replace("<dvbi-types:", "<");
rawSlepString = rawSlepString.Replace("</dvbi-types:", "</");
rawSlepString = rawSlepString.Replace("<dvbisdt:", "<");
rawSlepString = rawSlepString.Replace("</dvbisdt:", "</");
ServiceListEntryPoints serviceListEntryPoints = DvbIUtils.UnpackServiceListEntryPoints(rawSlepString);
EventHandler?.OnServiceListEntryPoints(CurrentCarrierInformation, serviceListEntryPoints, currentTime.Value, this);
return true;
}
else
{
return false;
}
break;
case "urn:dvb:metadata:nativeip:TimeOffsetFile":
TimeOffsetFileType timeOffsetFile = DvbNipUtilities.UnpackTimeOffsetFile(fluteListener.ToStream());
EventHandler?.OnTimeOffsetFile(CurrentCarrierInformation, timeOffsetFile);
return true;
case "urn:dvb:metadata:nativeip:NetworkInformationFile":
NetworkInformationFileType networkInformationFile = DvbNipUtilities.UnpackNetworkInformationFile(fluteListener.ToStream());
EventHandler?.OnNetworkInformationFile(CurrentCarrierInformation, networkInformationFile);
return true;
case "urn:dvb:metadata:nativeip:ServiceInformationFile":
ServiceInformationFileType serviceInformationFile = DvbNipUtilities.UnpackServiceInformationFile(fluteListener.ToStream());
EventHandler?.OnServiceInformationFile(CurrentCarrierInformation, serviceInformationFile);
return true;
case "urn:dvb:metadata:nativeip:ServiceGuide":
//Unfortunately, the NIPServiceGuideManifest does not contain any useful information at all, which is why we ignore it.
//There doesn't seem to be a way to get the actual EIT XML from it.
return true;
default:
throw new NotImplementedException(fluteListener.FileAssociation.ContentLocation);
}
}
private void TryPruneCache()
{

View File

@ -1,10 +1,14 @@
using System;
using log4net;
using MimeKit;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Ietf.FLUTE;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using skyscraper8.Ietf.FLUTE;
namespace skyscraper8.DvbNip
{
@ -12,8 +16,60 @@ namespace skyscraper8.DvbNip
{
private DvbNipUtilities() { }
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static XmlSerializer multicastGatewayConfigurationXmlSerializer;
public static bool IsXml(Stream s)
{
byte byteA = s.ReadUInt8();
byte byteB = s.ReadUInt8();
byte byteC = s.ReadUInt8();
byte byteD = s.ReadUInt8();
byte byteE = s.ReadUInt8();
byte byteF = s.ReadUInt8();
s.Position -= 6;
if (byteA != '<')
return false;
if (byteB != '?')
return false;
if (byteC != 'x')
return false;
if (byteD != 'm')
return false;
if (byteE != 'l')
return false;
if (byteF != ' ')
return false;
return true;
}
private static readonly byte[] MimeVersionMagic = new byte[]
{
0x4D, 0x49, 0x4D, 0x45, 0x2D, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x20
};
public static bool IsMime(Stream s)
{
if (s.GetAvailableBytes() <= MimeVersionMagic.Length)
{
return false;
}
byte[] buffer = new byte[MimeVersionMagic.Length];
int bufferReadResult = s.Read(buffer, 0, buffer.Length);
bool isMime = Enumerable.SequenceEqual(buffer, MimeVersionMagic);
s.Position -= bufferReadResult;
return isMime;
}
public static MulticastGatewayConfigurationType UnpackMulticastGatewayConfiguration(Stream stream)
{
if (multicastGatewayConfigurationXmlSerializer == null)
@ -120,6 +176,10 @@ namespace skyscraper8.DvbNip
{
bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
}
else if (bufferString.Contains("urn:dvb:metadata:nativeip:2022"))
{
bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2022", "urn:dvb:metadata:nativeip:2024");
}
object v = networkInformationFileXmlSerializer.Deserialize(new StringReader(bufferString));
NetworkInformationFileType result = (NetworkInformationFileType)v;
@ -141,10 +201,50 @@ namespace skyscraper8.DvbNip
{
bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2023", "urn:dvb:metadata:nativeip:2024");
}
else if (bufferString.Contains("urn:dvb:metadata:nativeip:2022"))
{
bufferString = bufferString.Replace("urn:dvb:metadata:nativeip:2022", "urn:dvb:metadata:nativeip:2024");
}
object v = serviceInformationFileXmlSerializer.Deserialize(new StringReader(bufferString));
ServiceInformationFileType result = (ServiceInformationFileType)v;
return result;
}
public static Stream GetFirstMimeBodyPart(Stream stream)
{
MimeMessage message = MimeMessage.Load(stream);
foreach (MimeEntity bodyPart in message.BodyParts)
{
MimePart part = bodyPart as MimePart;
if (part.ContentType.MimeType.Equals("application/mbms-envelope+xml"))
continue;
if (part.ContentType.MimeType.Equals("application/mbms-user-service-description+xml"))
continue;
if (part.ContentType.MimeType.Equals("application/mbms-schedule+xml"))
continue;
return part.Content.Stream;
}
throw new NotImplementedException();
}
public static FluteListener PatchMpegDashSegment(FluteListener listener)
{
Stream stream = listener.ToStream();
stream.Position = stream.Length - 1;
byte readUInt8 = stream.ReadUInt8();
if (readUInt8 != 0)
return listener;
logger.WarnFormat("MPEG-DASH segment {0} is overdumped. It will be patched to become playable.", listener.FileAssociation.ContentLocation);
stream.Position = stream.Length - 1;
while (stream.ReadUInt8() == 0)
{
stream.Position -= 2;
}
listener.TrimmedLength = stream.Position;
return listener;
}
}
}

View File

@ -21,6 +21,15 @@ namespace skyscraper8.DvbNip
NipStreamProviderName = Encoding.UTF8.GetString(ms.ReadBytes(length));
}
public NipActualCarrierInformation(ushort nipNetworkId, ushort nipCarrierId, ushort nipLinkId, ushort nipServiceId, string nipStreamProviderName)
{
NipNetworkId = nipNetworkId;
NipCarrierId = nipCarrierId;
NipLinkId = nipLinkId;
NipServiceId = nipServiceId;
NipStreamProviderName = nipStreamProviderName;
}
public ushort NipNetworkId { get; }
public ushort NipCarrierId { get; }
public ushort NipLinkId { get; }

View File

@ -0,0 +1,82 @@
using skyscraper5.Skyscraper;
namespace skyscraper8.GSE;
public class BBHeader : Validatable
{
public BBHeader(byte[] bbframe, int offset)
{
ReadOnlySpan<byte> BbHeaderSpan = new ReadOnlySpan<byte>(bbframe, offset, 10);
//byte 1, MATYPE-1
TsGs = (BbHeaderSpan[0] & 0xc0) >> 6;
SisMis = (BbHeaderSpan[0] & 0x20) != 0; //1 single, 0 multi
CcmAcm = (BbHeaderSpan[0] & 0x10) != 0; //1 ccm, 0 acm
Issyi = (BbHeaderSpan[0] & 0x08) != 0; //
Npd = (BbHeaderSpan[0] & 0x04) != 0;
Ro = (BbHeaderSpan[0] & 0x03);
Matype2 = (BbHeaderSpan[1]);
UserPacketLength = BbHeaderSpan[2] << 8 | BbHeaderSpan[3];
UserPacketLength /= 8;
DataFieldLength = BbHeaderSpan[4] << 8 | BbHeaderSpan[5];
DataFieldLength /= 8;
SyncByte = BbHeaderSpan[6];
SyncD = BbHeaderSpan[7] << 8 | BbHeaderSpan[8];
SyncD /= 8;
//ChecksumValid = DvbCrc8.Compute(BbHeaderSpan) == 0;
//Valid = ChecksumValid;
Valid = true;
}
public int SyncD { get; private set; }
public byte SyncByte { get; private set; }
public int DataFieldLength { get; private set; }
public int UserPacketLength { get; private set; }
/// <summary>
/// If MIS, this is the Input Stream Identifier
/// </summary>
public byte Matype2 { get; private set; }
/// <summary>
/// Transmission Roll-off factor
/// </summary>
public int Ro { get; private set; }
/// <summary>
/// Null Packet Deletion
/// </summary>
public bool Npd { get; private set; }
/// <summary>
/// If true, the ISSY field is inserted after UP
/// </summary>
public bool Issyi { get; set; }
/// <summary>
/// True if CCM, False if ACM
/// </summary>
public bool CcmAcm { get; private set; }
/// <summary>
/// True if SIS, False if MIS
/// </summary>
public bool SisMis { get; private set; }
/// <summary>
/// 0 = GS, packetized
/// 1 = GS, continuous
/// 2 = GSE-HEM
/// 3 = Transport
/// </summary>
public int TsGs { get; private set; }
}

View File

@ -0,0 +1,47 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE;
class BbframeDeencapsulator3 : IBbframeDeencapsulator
{
public BbframeDeencapsulator3(GsContextDto context)
{
this.context = context;
}
private readonly GsContextDto context;
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private long numPushed;
public void PushPacket(byte[] bbframe)
{
//byte 0, sync byte
if (bbframe[0] != 0xb8)
{
if (numPushed == 0)
{
logger.InfoFormat("The stream started in the middle of a BBFrame, let's skip to the start of the next one.");
}
return;
}
numPushed++;
BBHeader bbHeader = new BBHeader(bbframe, 1);
if (!bbHeader.Valid)
return;
if (mis == null)
mis = new IMisHandler[256];
if (mis[bbHeader.Matype2] == null)
{
logger.InfoFormat("Found a stream on MIS {0}",bbHeader.Matype2);
mis[bbHeader.Matype2] = new GsTypeDetector(context.MisClone(bbHeader.Matype2));
}
mis[bbHeader.Matype2].PushFrame(bbHeader, new ReadOnlySpan<byte>(bbframe, 11, bbframe.Length - 11));
}
private IMisHandler[] mis;
}

26
skyscraper8/GS/Crc8.cs Normal file
View File

@ -0,0 +1,26 @@
namespace skyscraper8.GSE;
public static class DvbCrc8
{
private const byte Polynomial = 0xD5; // x^8 + x^7 + x^6 + x^4 + x^2 + 1
public static byte Compute(ReadOnlySpan<byte> data)
{
byte crc = 0x00;
foreach (byte b in data)
{
crc ^= b; // XOR byte into top of crc register
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80) != 0)
crc = (byte)((crc << 1) ^ Polynomial);
else
crc <<= 1;
}
}
return crc;
}
}

View File

@ -0,0 +1,247 @@
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.IO;
using skyscraper8.GSE;
using skyscraper8.GSE.GSE;
using skyscraper8.Skyscraper.IO;
using skyscraper8.Skyscraper.Scraper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel;
namespace skyscraper8.GS.GSE_BFBS
{
internal class BfbsGseReader : IMisHandler
{
private GseLabel lastLabel;
private int frameNo;
public GsContextDto Context { get; set; }
public BfbsGseReader(GsContextDto context)
{
this.Context = context;
}
private bool CheckAllZeroes(ReadOnlySpan<byte> span)
{
for (int i = 0; i < span.Length - 4; i++)
{
if (span[i] != 0)
{
return false;
}
}
return true;
}
public void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan)
{
frameNo++;
bool validCrc = DvbCrc32.ValidateCrc(readOnlySpan);
if (!validCrc)
return;
if (readOnlySpan[0] == 0 && readOnlySpan[1] == 0 && readOnlySpan[2] == 0 && readOnlySpan[3] == 0)
{
if (CheckAllZeroes(readOnlySpan))
{
return;
}
else
{
throw new NotImplementedException("A BBFrame with data offset not at 0 was found. This not supported yet, please share a sample of this.");
}
}
StreamlikeSpan span = new StreamlikeSpan(readOnlySpan);
while (span.GetAvailableBytes() > 4)
{
byte byteA = span.ReadUInt8();
bool startIndicator = (byteA & 0x80) != 0;
bool endIndicator = (byteA & 0x40) != 0;
int labelTypeIndicator = (byteA & 0x30) >> 4;
if (!startIndicator && !endIndicator && labelTypeIndicator == 0)
{
//padding bits, packet has ended.
return;
}
else
{
GsePacket gsePacket = new GsePacket(startIndicator, endIndicator, labelTypeIndicator);
int gseLength = (byteA & 0x0f) << 8;
gseLength += span.ReadUInt8();
gsePacket.FragmentId = null;
if (!startIndicator || !endIndicator)
{
gsePacket.FragmentId = span.ReadUInt8();
gseLength -= 1;
}
if ((startIndicator) && !endIndicator)
{
gsePacket.TotalLength = span.ReadUInt16BE();
gseLength -= 2;
}
if (startIndicator)
{
gsePacket.ProtocolType = span.ReadUInt16BE();
gseLength -= 2;
if (labelTypeIndicator == 0)
{
gsePacket.Label = new _6byteLabel(span.ReadBytes(6));
gseLength -= 6;
}
else if (labelTypeIndicator == 1)
{
gsePacket.Label = new _3byteLabel(span.ReadBytes(3));
gseLength -= 3;
}
else if (labelTypeIndicator == 2)
{
gsePacket.Label = BroadcastLabel.GetInstance();
}
else if (labelTypeIndicator == 3)
{
gsePacket.Label = lastLabel;
}
lastLabel = gsePacket.Label;
}
if (!startIndicator && endIndicator)
gseLength -= 4;
gsePacket.GseDataBytes = span.ReadBytes(gseLength);
if (!startIndicator && endIndicator)
{
gsePacket.Crc32 = span.ReadUInt32BE();
}
HandleGseFrame(gsePacket);
}
}
}
private GseFragmentation[] fragmentations;
private void HandleGseFrame(GsePacket gsePacket)
{
if (gsePacket.StartIndicator & gsePacket.EndIndicator)
{
HandleAssembledFrame(gsePacket.ProtocolType.Value, gsePacket.GseDataBytes, gsePacket.Label);
return;
}
if (!gsePacket.StartIndicator && gsePacket.EndIndicator)
{
if (fragmentations == null)
{
//This stream had no fragmentations yet, so we cannot reassemble this packet.
return;
}
if (fragmentations[gsePacket.FragmentId.Value] == null)
{
//The previous fragments are missing, so we cannot reassemble this packet.
return;
}
fragmentations[gsePacket.FragmentId.Value].AddFragement(gsePacket);
if (fragmentations[gsePacket.FragmentId.Value].Validate())
{
byte[] gseDataBytes = fragmentations[gsePacket.FragmentId.Value].GetGseDataBytes();
ushort protocolType = fragmentations[gsePacket.FragmentId.Value].ProtocolType;
HandleAssembledFrame(protocolType, gseDataBytes, gsePacket.Label);
fragmentations[gsePacket.FragmentId.Value] = null;
return;
}
else
{
throw new NotImplementedException();
}
}
if (gsePacket.StartIndicator && !gsePacket.EndIndicator)
{
fragmentations = new GseFragmentation[256];
fragmentations[gsePacket.FragmentId.Value] = new GseFragmentation(gsePacket);
return;
}
if (!gsePacket.StartIndicator && !gsePacket.EndIndicator)
{
if (fragmentations == null)
{
//This stream had no fragmentations yet, so we cannot reassemble this packet.
return;
}
if (fragmentations[gsePacket.FragmentId.Value] == null)
{
//The previous fragments are missing, so we cannot reassemble this packet.
return;
}
fragmentations[gsePacket.FragmentId.Value].AddFragement(gsePacket);
return;
}
throw new NotImplementedException(
"A unknown frame type was encountered. Please share a sample of this stream.");
}
private void HandleAssembledFrame(ushort protocolType, byte[] buffer, GseLabel label)
{
switch (protocolType)
{
case 0x0081:
//Network clock reference
HandleNetworkClockReference(buffer);
return;
case 0x0082:
//Lower Layer Signalling, see en_30154502v010401p.pdf, page 49
HandleLowerLayerSignalling(buffer, label);
return;
case 0x0091:
//according to https://www.iana.org/assignments/ule-next-headers/ule-next-headers.xhtml it's private
return;
case 0x0800:
Context.IpOutput.OnIpDatagram(0x010e, buffer);
return;
default:
throw new NotImplementedException(protocolType.ToString());
}
}
private GseL2SHandler rcs2;
private void HandleLowerLayerSignalling(byte[] buffer, GseLabel label)
{
if (rcs2 == null)
{
rcs2 = new GseL2SHandler(Context);
}
rcs2.PushPacket(buffer, label);
}
private void HandleNetworkClockReference(byte[] buffer)
{
MemoryStream binaryReader = new MemoryStream(buffer);
uint pcrA = binaryReader.ReadUInt32BE();
ushort pcrB = binaryReader.ReadUInt16BE();
ulong pcr_base = ((ulong)pcrA << 1) | ((ulong)pcrB >> 15);
ulong pcr_ext = (ulong)pcrB & 0x01ff;
ulong PCR = pcr_base * 300 + pcr_ext;
ulong seconds = PCR / 27000000;
}
}
}

View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.GSE.GSE;
using skyscraper8.InteractionChannel;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
namespace skyscraper8.GS.GSE_BFBS
{
internal class GseL2SHandler
{
public GsContextDto Context { get; }
public GseL2SHandler(GsContextDto context)
{
Context = context;
}
public void PushPacket(byte[] buffer, GseLabel label)
{
GseTableStructure gseTableStructure = new GseTableStructure(buffer);
if (!gseTableStructure.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.GseTableStructureInvalid);
return;
}
MemoryStream ms = new MemoryStream(gseTableStructure.TableContent, false);
switch (gseTableStructure.TableId)
{
case 0x40:
RcsNit nit = new RcsNit(ms);
if (!nit.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.NitInvalid);
return;
}
Context.Rcs2Output.OnRcs2Nit(gseTableStructure.InteractiveNetworkId, nit);
return;
case 0x41:
Rmt rmt = new Rmt(ms, true);
if (!rmt.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.RmtInvalid);
return;
}
Context.Rcs2Output.OnRcsMap(rmt);
return;
case 0x70:
Rcs2Tdt tdt = new Rcs2Tdt(ms);
if (!tdt.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.TdtInvalid);
return;
}
Context.Rcs2Output.OnRcs2Tdt(gseTableStructure.InteractiveNetworkId, tdt);
return;
case 0xa0:
Sct sct = new Sct(ms);
if (!sct.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.SctInvalid);
return;
}
Context.Rcs2Output.OnSuperframeComposition(gseTableStructure.InteractiveNetworkId, sct);
return;
case 0xa3:
Spt spt = new Spt(ms);
if (!spt.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.SptInvalid);
return;
}
Context.Rcs2Output.OnSatellitePosition(gseTableStructure.InteractiveNetworkId, spt);
return;
case 0xa4:
Cmt cmt = new Cmt(ms);
if (!cmt.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.CmtInvalid);
return;
}
Context.Rcs2Output.OnCorrectionMessage(gseTableStructure.InteractiveNetworkId, cmt);
return;
case 0xab:
Fct2 fct2 = new Fct2(ms);
if (!fct2.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Fct2Invalid);
return;
}
Context.Rcs2Output.OnFrameComposition2(gseTableStructure.InteractiveNetworkId, fct2);
return;
case 0xac:
Bct bct = new Bct(ms);
if (!bct.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.BctInvalid);
return;
}
Context.Rcs2Output.OnBroadcastConfiguration(gseTableStructure.InteractiveNetworkId, bct);
return;
case 0xad:
Tbtp2 tbtp2 = new Tbtp2(ms);
if (!tbtp2.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Tbtp2Invalid);
return;
}
Context.Rcs2Output.OnTerminalBurstTimePlan2(gseTableStructure.InteractiveNetworkId, tbtp2);
return;
case 0xae:
int rmtTransmissionStandard = Context.Rcs2Output.GetRmtTransmissionStandard(gseTableStructure.InteractiveNetworkId);
Tmst2 tmst2 = new Tmst2(ms, rmtTransmissionStandard);
if (!tmst2.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Tmst2Invalid);
return;
}
Context.Rcs2Output.OnTransmissionModeSupport2(gseTableStructure.InteractiveNetworkId, tmst2);
return;
case 0xb0:
Tim tim = new Tim(ms, label.IsBroadcast());
if (!tim.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.TimInvalid);
return;
}
PhysicalAddress physicalAddress;
if (label.IsBroadcast())
{
physicalAddress = _6byteLabel.BROADCAST;
}
else if (label.Length == 6)
{
physicalAddress = ((_6byteLabel)label).MAC;
}
else
{
throw new NotImplementedException("Found a TIM-U Table with a 3-byte GSE Label in this stream. This is not supported yet, please share a sample of this stream.");
}
tim.Handle(physicalAddress, gseTableStructure.InteractiveNetworkId, Context.Rcs2Output);
return;
case 0xc0:
//User defined, we have no way of knowing what this is.
return;
default:
//See en_30154502v010401p.pdf
//page 49
throw new NotImplementedException(String.Format(
"Unknown DVB-RCS2 Table Id: 0x{0:X2}\nIf this is reproducible on this stream, please consider submitting me a sample.",
gseTableStructure.TableId));
}
}
}
}

View File

@ -0,0 +1,115 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Skyscraper.IO;
using skyscraper8.GS;
using skyscraper8.GSE.GSE;
namespace skyscraper8.GSE.GSE_HEM;
class GseHemReader : IMisHandler
{
public GseHemReader(GsContextDto context)
{
rayBuffer = new RayBuffer();
this.Context = context;
}
private GseLabel lastLabel;
private RayBuffer rayBuffer;
private const int BUFFER_THRESHOLD = 65536 * 2; //Twice the maximum PDU size of GSE. That way we can guarantee we have one full PDU in buffer.
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public GsContextDto Context { get; set; }
public void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan)
{
MemoryStream ms = new MemoryStream(readOnlySpan.ToArray());
rayBuffer.Enqueue(ms, bbHeader.SyncD);
while (rayBuffer.AvailableBytes > BUFFER_THRESHOLD)
{
byte a = rayBuffer.ReadUInt8();
bool startIndicator = (a & 0x80) != 0;
bool endIndicator = (a & 0x40) != 0;
int labelTypeIndicator = (a & 0x30) >> 4;
if (!startIndicator && !endIndicator)
{
//Padding packets probably shouldn't happen in HEM.
rayBuffer.Resync();
return;
}
else
{
GsePacket child = new GsePacket(startIndicator, endIndicator, labelTypeIndicator);
int gseLength = (a & 0x0f) << 8;
gseLength += rayBuffer.ReadUInt8();
if (!startIndicator || !endIndicator)
{
//According to ETSI TS 102 606-1 V1.2.1, HEM does not fragment packets. So we lost our sync!
rayBuffer.Resync();
return;
}
if (startIndicator && !endIndicator)
{
throw new NotImplementedException("I didn't expect an open-ended GSE Packet in GSE-HEM. Please share a sample of this stream, so I can implement this.");
}
if (startIndicator)
{
child.ProtocolType = rayBuffer.ReadUInt16BE();
gseLength -= 2;
switch (labelTypeIndicator)
{
case 0:
child.Label = new _6byteLabel(rayBuffer.ReadBytes(6));
gseLength -= 6;
lastLabel = child.Label;
break;
case 1:
child.Label = new _3byteLabel(rayBuffer.ReadBytes(3));
gseLength -= 3;
lastLabel = child.Label;
break;
case 2:
child.Label = BroadcastLabel.GetInstance();
break;
case 3:
child.Label = lastLabel;
break;
default:
throw new NotImplementedException(String.Format("Unknown label type indicator: {0}", labelTypeIndicator));
}
}
if (!startIndicator && endIndicator)
gseLength -= 4;
child.GseDataBytes = rayBuffer.ReadBytes(gseLength);
if (!startIndicator && endIndicator)
throw new NotImplementedException("I didn't expect a GSE packet with a missing head in GSE-HEM. Please share a sample of this stream, so I can implement this.");
HandlePacket(child);
}
}
}
private void HandlePacket(GsePacket packet)
{
if (packet.StartIndicator && packet.EndIndicator)
{
switch (packet.ProtocolType)
{
case 0x0800:
Context.IpOutput.OnIpDatagram(0x010e,packet.GseDataBytes);
return;
default:
logger.WarnFormat("This GSE-HEM stream contains traffic other than IP. IP is type 0x0800, but we got 0x{0:X4} here.", packet.ProtocolType);
return;
}
}
throw new NotImplementedException(packet.ToString());
}
}

View File

@ -0,0 +1,111 @@
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.GSE.GSE_HEM;
public class RayBuffer
{
private MemoryStream currentItem;
private Queue<Tuple<MemoryStream, int>> queue;
public int QueuedItems
{
get
{
int result = 0;
if (currentItem != null)
result++;
if (queue != null)
result += queue.Count;
return result;
}
}
public void Enqueue(MemoryStream ms, int syncPoint)
{
if (currentItem == null)
{
currentItem = ms;
currentItem.Position = syncPoint;
return;
}
else
{
if (queue == null)
queue = new Queue<Tuple<MemoryStream, int>>();
queue.Enqueue(new Tuple<MemoryStream, int>(ms, syncPoint));
}
}
public long AvailableBytes
{
get
{
if (currentItem == null)
return 0;
long result = currentItem.GetAvailableBytes();
if (queue != null)
{
result += queue.Select(x => x.Item1.GetAvailableBytes()).Sum();
}
return result;
}
}
public byte ReadUInt8()
{
byte[] tmp = new byte[1];
if (Read(tmp, 0, 1) != 1)
throw new IOException("ReadUInt8 failed");
return tmp[0];
}
private int Read(byte[] outBuffer, int offset, int count)
{
int result = 0;
while (count > 0)
{
int stepSize = Math.Min(count, (int)currentItem.GetAvailableBytes());
int stepResult = currentItem.Read(outBuffer, offset, stepSize);
offset += stepResult;
count -= stepResult;
result += stepResult;
if (currentItem.GetAvailableBytes() == 0)
{
currentItem = queue.Dequeue().Item1;
}
}
return result;
}
public ushort ReadUInt16BE()
{
byte[] tmp = new byte[2];
if (Read(tmp, 0, 2) != 2)
throw new IOException("ReadUInt16BE failed");
if (BitConverter.IsLittleEndian)
(tmp[0], tmp[1]) = (tmp[1], tmp[0]);
return BitConverter.ToUInt16(tmp, 0);
}
public byte[] ReadBytes(int p0)
{
byte[] tmp = new byte[p0];
if (Read(tmp, 0, p0) != p0)
throw new IOException(String.Format("Reading {0} bytes failed.", p0));
return tmp;
}
public void Resync()
{
Tuple<MemoryStream, int> tuple = queue.Dequeue();
currentItem = tuple.Item1;
currentItem.Position = tuple.Item2;
if (currentItem.Position > currentItem.Length)
{
Resync();
}
}
}

View File

@ -0,0 +1,55 @@
namespace skyscraper8.GSE.GSE;
public class GseFragmentation
{
public GseFragmentation(GsePacket child)
{
this.ProtocolType = child.ProtocolType.Value;
this.Label = child.Label;
AddFragement(child);
this.TotalLength = child.TotalLength.Value;
this.Crc32 = child.Crc32;
}
public uint Crc32 { get; set; }
public ushort TotalLength { get; set; }
private List<byte[]> fragments;
public bool Validate()
{
//TODO: Implement proper CRC-32 checking here
return true;
}
public void AddFragement(GsePacket packet)
{
if (fragments == null)
fragments = new List<byte[]>();
fragments.Add(packet.GseDataBytes);
}
public GseLabel Label { get; private set; }
public ushort ProtocolType { get; private set; }
/// <summary>
/// Assembles the payload
/// </summary>
/// <returns>The concatenated GSE Data Bytes of each packet.</returns>
public byte[] GetGseDataBytes()
{
int totalLength = fragments.Select(x => x.Length).Sum();
byte[] buffer = new byte[totalLength];
int offset = 0;
for (int i = 0; i < fragments.Count; i++)
{
Array.Copy(fragments[i],0,buffer,offset,fragments[i].Length);
offset += fragments[i].Length;
}
return buffer;
}
}

View File

@ -0,0 +1,106 @@
using System.Net.NetworkInformation;
namespace skyscraper8.GSE.GSE;
public abstract class GseLabel
{
public abstract int Length { get; }
public abstract string ToString();
public abstract int LabelTypeIndicator { get; }
public abstract bool IsBroadcast();
}
public class _6byteLabel : GseLabel
{
public _6byteLabel(byte[] buffer)
{
MAC = new PhysicalAddress(buffer);
}
public _6byteLabel(PhysicalAddress macAddress)
{
MAC = macAddress;
}
public PhysicalAddress MAC { get; private set; }
override public string ToString()
{
return MAC.ToString();
}
public override int Length => 6;
public override int LabelTypeIndicator => 0;
public static PhysicalAddress BROADCAST = new PhysicalAddress(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff });
public override bool IsBroadcast()
{
return MAC.Equals(BROADCAST);
}
}
public class _3byteLabel : GseLabel
{
public _3byteLabel(byte[] buffer)
{
this.buffer = buffer;
}
private byte[] buffer;
public byte GroupId => buffer[0];
public ushort LogonId
{
get
{
ushort result = buffer[1];
result <<= 8;
result += buffer[2];
return result;
}
}
public override string ToString()
{
return $"{nameof(GroupId)}: {GroupId}, {nameof(LogonId)}: {LogonId}";
}
override public int Length => 3;
public override int LabelTypeIndicator => 1;
public override bool IsBroadcast()
{
return buffer[1] == 0xff && buffer[2] == 0xff;
}
}
public class BroadcastLabel : GseLabel
{
private BroadcastLabel() {}
override public string ToString()
{
return "<Broadcast>";
}
override public int Length => 0;
private static BroadcastLabel _instance;
public static BroadcastLabel GetInstance()
{
if (_instance == null)
{
_instance = new BroadcastLabel();
}
return _instance;
}
public override int LabelTypeIndicator => 2;
public override bool IsBroadcast()
{
return true;
}
}

View File

@ -0,0 +1,27 @@
namespace skyscraper8.GSE.GSE;
public class GsePacket
{
public bool StartIndicator { get; }
public bool EndIndicator { get; }
public int LabelTypeIndicator { get; }
public byte? FragmentId { get; set; }
public ushort? TotalLength { get; set; }
public ushort? ProtocolType { get; set; }
public GseLabel Label { get; set; }
public byte[] GseDataBytes { get; set; }
public uint Crc32 { get; set; }
public GsePacket(bool startIndicator, bool endIndicator, int labelTypeIndicator)
{
StartIndicator = startIndicator;
EndIndicator = endIndicator;
LabelTypeIndicator = labelTypeIndicator;
}
public override string ToString()
{
return
$"{nameof(StartIndicator)}: {StartIndicator}, {nameof(EndIndicator)}: {EndIndicator}, {nameof(LabelTypeIndicator)}: {LabelTypeIndicator}, {nameof(FragmentId)}: {FragmentId}, {nameof(TotalLength)}: {TotalLength}, {nameof(ProtocolType)}: {ProtocolType}, {nameof(Label)}: {Label}";
}
}

View File

@ -0,0 +1,173 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Skyscraper.IO;
using skyscraper8.GS;
namespace skyscraper8.GSE.GSE;
internal class GseReader : IMisHandler
{
private GseLabel lastLabel;
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public GsContextDto Context { get; set; }
public void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan)
{
MemoryStream ms = new MemoryStream(readOnlySpan.ToArray());
while (ms.Position < ms.Length)
{
byte a = ms.ReadUInt8();
bool startIndicator = (a & 0x80) != 0;
bool endIndicator = (a & 0x40) != 0;
int labelTypeIndicator = (a & 0x30) >> 4;
if (!startIndicator && !endIndicator && labelTypeIndicator == 0)
{
//end of BBFrame
return;
}
else
{
int gseLength = (a & 0x0f);
gseLength <<= 8;
gseLength += ms.ReadUInt8();
GsePacket child = new GsePacket(startIndicator, endIndicator, labelTypeIndicator);
if (!startIndicator || !endIndicator)
{
if (ms.Position == ms.Length)
return;
child.FragmentId = ms.ReadUInt8();
gseLength--;
}
if (startIndicator && !endIndicator)
{
child.TotalLength = ms.ReadUInt16BE();
gseLength -= 2;
}
if (startIndicator)
{
if (ms.GetAvailableBytes() < 2)
return;
child.ProtocolType = ms.ReadUInt16BE();
gseLength -= 2;
if (!endIndicator)
child.TotalLength -= 2;
switch (labelTypeIndicator)
{
case 0:
if (ms.GetAvailableBytes() < 6)
return;
child.Label = new _6byteLabel(ms.ReadBytes(6));
gseLength -= 6;
if (!endIndicator)
child.TotalLength -= 6;
lastLabel = child.Label;
break;
case 1:
if (ms.GetAvailableBytes() < 3)
return;
child.Label = new _3byteLabel(ms.ReadBytes(3));
gseLength -= 3;
if (!endIndicator)
child.TotalLength -= 3;
lastLabel = child.Label;
break;
case 2:
child.Label = BroadcastLabel.GetInstance();
break;
case 3:
child.Label = this.lastLabel;
break;
default:
throw new NotImplementedException(String.Format("Unknown label type: {0}", labelTypeIndicator));
}
}
if (!startIndicator && endIndicator)
gseLength -= 4; //for crc32
if (gseLength > ms.GetAvailableBytes())
return;
if (gseLength <= -1)
return;
child.GseDataBytes = ms.ReadBytes(gseLength);
if (!startIndicator && endIndicator)
child.Crc32 = ms.ReadUInt32BE();
ProcessPacket(child);
}
}
}
private bool[] warnedEthertypes;
private GseFragmentation[] fragmentations;
private void ProcessPacket(GsePacket child)
{
if (!child.StartIndicator && child.EndIndicator)
{
if (fragmentations == null)
return;
GseFragmentation currentFragmentation = fragmentations[child.FragmentId.Value];
if (currentFragmentation == null)
return;
currentFragmentation.AddFragement(child);
if (currentFragmentation.Validate())
{
Context.IpOutput.OnIpDatagram(0x010e,currentFragmentation.GetGseDataBytes());
}
fragmentations[child.FragmentId.Value] = null;
return;
}
if (child.StartIndicator && !child.EndIndicator)
{
if (fragmentations == null)
fragmentations = new GseFragmentation[256];
fragmentations[child.FragmentId.Value] = new GseFragmentation(child);
return;
}
if (!child.StartIndicator && !child.EndIndicator)
{
if (fragmentations == null)
{
//We haven't seen any starting frames yet, so we can't use an inbetween frame. We can throw this away.
return;
}
if (fragmentations[child.FragmentId.Value] == null)
{
//fragment beginning missing, we can throw this away.
return;
}
throw new NotImplementedException(child.ToString());
}
if (child.StartIndicator && child.EndIndicator)
{
switch (child.ProtocolType)
{
case 0x0800:
Context.IpOutput.OnIpDatagram(0x010e, child.GseDataBytes);
break;
default:
if (warnedEthertypes == null)
warnedEthertypes = new bool[ushort.MaxValue];
if (!warnedEthertypes[child.ProtocolType.Value])
{
logger.WarnFormat("This GSE contains other traffic types (type 0x{0:X4}) besides IP (type 0x0800). If it's not too much trouble, please consider submitting a sample.",child.ProtocolType.Value);
warnedEthertypes[child.ProtocolType.Value] = true;
}
break;
}
return;
}
throw new NotImplementedException(child.ToString());
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.src.InteractionChannel;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GS
{
internal class GsContextDto
{
public GsContextMisDto MisClone(byte mis)
{
GsContextMisDto misClone = new GsContextMisDto(mis);
Type type = this.GetType();
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
object? value = propertyInfo.GetValue(this);
propertyInfo.SetValue(misClone, value);
}
return misClone;
}
public byte GetMisId()
{
GsContextMisDto misDto = this as GsContextMisDto;
if (misDto == null)
return 0;
return misDto.Mis;
}
public ISubTsHandler TsOutput { get; set; }
public IMultiprotocolEncapsulationEventHandler IpOutput { get; set; }
public InteractionChannelHandler Rcs2Output { get; set; }
}
internal class GsContextMisDto : GsContextDto
{
public byte Mis { get; }
public GsContextMisDto(byte mis)
{
Mis = mis;
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.GS
{
public class GsException : Exception
{
public GsException()
{
}
public GsException(string message) : base(message)
{
}
public GsException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,109 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper8.GS;
using skyscraper8.GS.GSE_BFBS;
using skyscraper8.GS.SiminnRadiomidun;
using skyscraper8.GSE.GSE_HEM;
using skyscraper8.GSE.GSE;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE;
class GsTypeDetector : IMisHandler
{
public GsContextDto Context { get; set; }
private readonly ILog logger;
private HashSet<Tuple<int, byte>> _postedStreamTypes;
public GsTypeDetector(GsContextMisDto context)
{
this.logger = LogManager.GetLogger(String.Format("{0} MIS {1}",System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name,context.Mis));
this.Context = context;
}
private GseReader gseReader;
private GseHemReader gseHemReader;
private SiminnRadiomidunReader siminnRadiomidunReader;
private BfbsGseReader bfbsGseReader;
private int simminRadiomidunScore;
private int bfbsScore;
public void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan)
{
if (readOnlySpan.Length == 0)
{
return;
}
if (bbHeader.TsGs == 1 && bbHeader.SyncByte == 0)
{
//Looks like Continuous GSE.
if (gseReader == null)
{
gseReader = new GseReader();
gseReader.Context = Context;
}
gseReader.PushFrame(bbHeader, readOnlySpan);
return;
}
if (bbHeader.TsGs == 2 && bbHeader.SyncByte == 0)
{
//Looks like GSE-HEM
if (gseHemReader == null)
{
gseHemReader = new GseHemReader(Context);
}
gseHemReader.PushFrame(bbHeader, readOnlySpan);
return;
}
if (bbHeader.TsGs == 0 && bbHeader.SyncByte == 71 && bbHeader.UserPacketLength == 188)
{
simminRadiomidunScore++;
if (simminRadiomidunScore > 10)
{
//Looks like a Simmin Radiomidun-like stream.
if (siminnRadiomidunReader == null)
{
//These behave similar to T2-MI. Interesting.
siminnRadiomidunReader = new SiminnRadiomidunReader(Context);
}
siminnRadiomidunReader.PushFrame(bbHeader, readOnlySpan);
return;
}
return;
}
else
{
if (simminRadiomidunScore < 10)
{
simminRadiomidunScore = 0;
}
}
if (bbHeader.TsGs == 1 && bbHeader.SyncByte == 1 && bbHeader.UserPacketLength == 0)
{
if (bfbsGseReader == null)
{
bfbsGseReader = new BfbsGseReader(Context);
}
bfbsGseReader.PushFrame(bbHeader, readOnlySpan);
return;
}
//We have no idea what this is.
Tuple<int, byte> streamTypeToPost = new Tuple<int, byte>(bbHeader.TsGs, bbHeader.SyncByte);
if (_postedStreamTypes == null)
_postedStreamTypes = new HashSet<Tuple<int, byte>>();
if (!_postedStreamTypes.Contains(streamTypeToPost))
{
logger.WarnFormat("This GS contains packets of type {0} ({2}) with a sync byte of {1}. This is not supported yet. If it isn't too much trouble, please consider sharing a sample of this stream.",streamTypeToPost.Item1,streamTypeToPost.Item2, (TsGsType)streamTypeToPost.Item1);
_postedStreamTypes.Add(streamTypeToPost);
}
}
}

View File

@ -0,0 +1,10 @@
using skyscraper8.GS;
namespace skyscraper8.GSE;
interface IMisHandler
{
public GsContextDto Context { get; set; }
void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan);
}

View File

@ -11,7 +11,7 @@ public class Pts2Bbf
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public static void Run(FileInfo file, bool useUdpDecap = false)
public static void Run(FileInfo file, bool useUdpDecap = false, IPEndPoint ipEndPoint = null)
{
if (!file.Exists)
{
@ -25,9 +25,13 @@ public class Pts2Bbf
string changeExtension = Path.ChangeExtension(file.FullName, ".sdecap");
BbfUdpDecap.UdpDecapFileOutput udpDecapSink = new BbfUdpDecap.UdpDecapFileOutput(new FileInfo(changeExtension));
if (ipEndPoint == null)
{
ipEndPoint = new IPEndPoint(IPAddress.Parse("239.199.2.1"), 1234);
}
BbfUdpDecap bbfUdpDecap = new BbfUdpDecap();
bbfUdpDecap.SetTargetPort(1234);
bbfUdpDecap.SetTargetIp(IPAddress.Parse("239.199.2.1"));
bbfUdpDecap.SetTargetPort(ipEndPoint.Port);
bbfUdpDecap.SetTargetIp(ipEndPoint.Address);
bbfUdpDecap.Sink = udpDecapSink;
dumper = bbfUdpDecap;
}
@ -40,7 +44,7 @@ public class Pts2Bbf
FileStream fileStream = file.OpenRead();
TsContext mpeg2 = new TsContext();
mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(new NullGsEventHandler(),dumper));
mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(null, dumper));
DataStorage dataStorage = new InMemoryScraperStorage();
ObjectStorage objectStorage = new NullObjectStorage();
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, dataStorage, objectStorage);

View File

@ -0,0 +1,62 @@
using log4net;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8.GSE;
public class Pts2Bbf2 : IBbframeDeencapsulator
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private readonly FileInfo _fi;
private readonly DirectoryInfo outputDir;
public Pts2Bbf2(FileInfo fi)
{
_fi = fi;
string outputPath = Path.Combine(_fi.Directory.FullName, Path.GetFileNameWithoutExtension(_fi.Name) + "_frames");
outputDir = new DirectoryInfo(outputPath);
outputDir.EnsureExists();
}
public void Run()
{
TsContext mpeg2 = new TsContext();
Stid135BbFrameReader bbFrameReader = new Stid135BbFrameReader(null, this);
mpeg2.RegisterPacketProcessor(0x010e, bbFrameReader);
SkyscraperContext context = new SkyscraperContext(mpeg2, new InMemoryScraperStorage(), new NullObjectStorage());
context.InitalizeFilterChain();
FileStream fileStream = _fi.OpenRead();
context.IngestFromStream(fileStream);
}
private int frameNo;
public void PushPacket(byte[] bbframe)
{
if (bbframe[0] != 0xb8)
{
return;
}
BBHeader bbHeader = new BBHeader(bbframe, 1);
if (!bbHeader.Valid)
return;
string fname = String.Format("Frame{0:D8}_TSGS{1}_MIS{2:D3}_SYNC{3:D3}.bbframe", ++frameNo,bbHeader.TsGs,bbHeader.SisMis ? bbHeader.Matype2 : 0,bbHeader.SyncByte);
string outfname = Path.Combine(outputDir.FullName, fname);
ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(bbframe, 11, bbframe.Length - 11);
byte[] buffer = readOnlySpan.ToArray();
File.WriteAllBytes(outfname, buffer);
if (IpPacketFinder.TestForHttpContents(buffer))
{
logger.InfoFormat("Found unencrypted HTTP traffic in {0}", fname);
}
}
}

View File

@ -0,0 +1,32 @@
using log4net;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8.GSE;
public class Stid135Test
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public static void Run(FileInfo file)
{
FileStream fileStream = file.OpenRead();
TsContext mpeg2 = new TsContext();
DataStorage dataStorage = new InMemoryScraperStorage();
ObjectStorage objectStorage = new FilesystemStorage(new DirectoryInfo("nip"));
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, dataStorage, objectStorage);
GsContextDto context = new GsContextDto();
mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(context));
skyscraper.InitalizeFilterChain();
skyscraper.IngestFromStream(fileStream);
fileStream.Close();
logger.Info("STiD135 Tester exiting");
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Skyscraper.IO;
using skyscraper5.T2MI;
using skyscraper8.GSE;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GS.SiminnRadiomidun
{
internal class SiminnRadiomidunReader : IMisHandler
{
public SiminnRadiomidunReader(GsContextDto context)
{
this.packetQueue = new Queue<Tuple<BBHeader,MemoryStream>>();
this.isInSync = false;
this.subTsKey = new SiminnRadiomidunSubTsIdentifier(context.GetMisId());
this.Context = context;
}
private bool isInSync;
private Queue<Tuple<BBHeader, MemoryStream>> packetQueue;
private MemoryStream currentItem;
private SiminnRadiomidunSubTsIdentifier subTsKey;
public long PacketQueueSize
{
get
{
if (packetQueue == null)
return 0;
return packetQueue.Select(x => x.Item2.Length).Sum();
}
}
public GsContextDto Context { get; set; }
public void PushFrame(BBHeader bbHeader, ReadOnlySpan<byte> readOnlySpan)
{
if (packetQueue == null)
packetQueue = new Queue<Tuple<BBHeader, MemoryStream>>();
packetQueue.Enqueue(new Tuple<BBHeader, MemoryStream>(bbHeader,new MemoryStream(readOnlySpan.Slice(0,bbHeader.DataFieldLength).ToArray(), true)));
while (PacketQueueSize > 8192)
{
if (!isInSync)
{
Tuple<BBHeader, MemoryStream> dequeue = packetQueue.Dequeue();
currentItem = dequeue.Item2;
currentItem.Position = dequeue.Item1.SyncD;
isInSync = true;
}
byte[] outputPacketBuffer = new byte[188];
int result = currentItem.Read(outputPacketBuffer, 0, 188);
if (result == 188)
{
OutputPacket(outputPacketBuffer);
}
else
{
int bytesNeeded = 188 - result;
Tuple<BBHeader, MemoryStream> dequeue = packetQueue.Dequeue();
if (bytesNeeded == dequeue.Item1.SyncD)
{
//We're still in sync!
syncSucess++;
currentItem = dequeue.Item2;
currentItem.Read(outputPacketBuffer, result, bytesNeeded);
OutputPacket(outputPacketBuffer);
}
else
{
//sync loss, let's discard the packet
syncFail++;
currentItem = dequeue.Item2;
currentItem.Position = dequeue.Item1.SyncD;
}
}
}
}
private ulong syncSucess, syncFail;
private void OutputPacket(byte[] buffer)
{
buffer[0] = 0x47;
Context.TsOutput.OnSubTsPacket(subTsKey, buffer);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.GS.SiminnRadiomidun
{
internal class SiminnRadiomidunSubTsIdentifier
{
public byte Mis { get; }
public SiminnRadiomidunSubTsIdentifier(byte misId)
{
Mis = misId;
}
protected bool Equals(SiminnRadiomidunSubTsIdentifier other)
{
return Mis == other.Mis;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((SiminnRadiomidunSubTsIdentifier)obj);
}
public override int GetHashCode()
{
return Mis.GetHashCode();
}
public override string ToString()
{
return $"{nameof(Mis)}{Mis}";
}
}
}

View File

@ -6,20 +6,21 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE
{
internal class Stid135BbFrameReader : ITsPacketProcessor
{
private IGsEventHandler mpeEventHandler;
private IBbframeDeencapsulator deencapsulator;
public Stid135BbFrameReader(IGsEventHandler mpeEventHandler, IBbframeDeencapsulator deencapsulator = null)
public Stid135BbFrameReader(GsContextDto context, IBbframeDeencapsulator deencapsulator = null)
{
if (deencapsulator == null)
deencapsulator = new BbframeDeencapsulator();
deencapsulator = new BbframeDeencapsulator3(context);
this.mpeEventHandler = mpeEventHandler;
this.deencapsulator = deencapsulator;
}
@ -56,21 +57,6 @@ namespace skyscraper8.GSE
{
outbuf.Write(packets, 9, packets[7] - 1);
}
/*if ((packets[8] & 0xff) == 0xb8)
{
if (outbuf != null)
{
byte[] chi = outbuf.ToArray();
deencapsulator.PushPacket(chi);
}
outbuf = new MemoryStream();
outbuf.Write(packets,9, packets[7] - 1);
}
else
{
if (outbuf != null)
outbuf.Write(packets, 9, packets[7] - 1);
}*/
}
}
}

View File

@ -0,0 +1,9 @@
namespace skyscraper8.GSE;
public enum TsGsType
{
GenericPacketized,
GenericContinuous,
GseHem,
Transport
}

View File

@ -1,446 +0,0 @@
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using log4net;
namespace skyscraper8.GSE
{
internal class BbframeDeencapsulator : IBbframeDeencapsulator
{
private bool interruptedGseHem;
private MemoryStream interruptedGseHemBuffer;
private bool interruptedGseHemWantsCrc32;
public IGsEventHandler MpeEventHandler { get; set; }
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private bool shownNonGseWarning;
public void PushPacket(byte[] bbframe)
{
MemoryStream ms = new MemoryStream(bbframe, false);
//BBHeader
byte matype1 = ms.ReadUInt8();
int tsGsField = (matype1 & 0xc0) >> 6;
bool sisMisField = (matype1 & 0x20) != 0;
bool ccmAcmField = (matype1 & 0x10) != 0;
bool issyi = (matype1 & 0x08) != 0;
bool npd = (matype1 & 0x04) != 0;
int ro = matype1 & 0x03;
byte matype2 = ms.ReadUInt8();
ushort userPacketLength = ms.ReadUInt16BE();
ushort dataFieldLength = ms.ReadUInt16BE();
byte sync = ms.ReadUInt8();
ushort syncd = ms.ReadUInt16BE();
byte crc8 = ms.ReadUInt8();
if (userPacketLength == 0 && dataFieldLength == 0)
return;
switch (tsGsField)
{
case 1:
if (sync != 0)
{
if (!shownNonGseWarning)
{
logger.WarnFormat("This stream is a valid GS, but also contains packets which are not GSE (type 0), but of type {0} . Please share a sample of this stream!", sync);
shownNonGseWarning = true;
}
return;
}
int bytes = dataFieldLength / 8;
if (ms.GetAvailableBytes() < bytes)
return;
HandleContinous(ms);
break;
case 2:
int hemBytes = dataFieldLength / 8;
if (ms.GetAvailableBytes() < hemBytes)
return;
HandleGseHem(ms);
break;
default: //0 = generic packetized, 1 = generic continouus, 2 = gse, 3 = ts
logger.Warn(string.Format("Unsupported: TS/GS field says 0x{0:X2}, please share a sample of this stream.", tsGsField));
break;
}
}
private GseFragmentation[] gseFragmentations;
private void HandleContinous(MemoryStream ms)
{
ms.Position = 10;
while (ms.GetAvailableBytes() > 0)
{
GsePacket packet = GsePacket.Read(ms);
if (packet.IsPadding)
{
break;
}
if (!packet.Valid)
{
break;
}
if (packet.IsCompletePacket)
{
if (ValidateEthertype(packet.ProtocolType.Value))
{
MpeEventHandler.OnIpDatagram(PID, packet.GseData);
}
else
{
logger.WarnFormat("Unknown EtherType: 0x{0:X4}", packet.ProtocolType.Value);
}
continue;
}
if (packet.StartIndicator && !packet.EndIndicator)
{
if (gseFragmentations == null)
gseFragmentations = new GseFragmentation[256];
gseFragmentations[packet.FragmentId.Value] = new GseFragmentation();
gseFragmentations[packet.FragmentId.Value].AddPacket(packet);
continue;
}
if (!packet.StartIndicator && packet.EndIndicator)
{
if (gseFragmentations == null)
continue;
if (gseFragmentations[packet.FragmentId.Value] == null)
continue;
gseFragmentations[packet.FragmentId.Value].AddPacket(packet);
continue;
}
if (!packet.StartIndicator && !packet.EndIndicator)
{
if (gseFragmentations == null)
continue;
throw new NotImplementedException();
}
throw new NotImplementedException();
}
}
private bool gseNeedMore;
private MemoryStream gseAssembler;
public int PID;
private void HandleGseHem(MemoryStream ms)
{
ms.Position = 10;
byte syncByte = ms.ReadUInt8();
ms.Position = 10;
long availableBytes = ms.GetAvailableBytes();
byte[] readBytes = ms.ReadBytes(availableBytes);
GseAssemblyState assemblyState = ValidateGse(gseAssembler, true);
if (assemblyState == GseAssemblyState.NOT_YET_BEGUN)
{
if ((syncByte & 0xc0) == 0xc0)
{
//Aktuell ist der GSE Assembler nicht beschäftigt, aber wir haben ein Sync-Byte, also kann er loslegen.
gseAssembler = new MemoryStream();
gseAssembler.Write(readBytes, 0, readBytes.Length);
return;
}
else
{
//Aktuell ist der GSE Assembler nicht beschäftigt, und wir haben kein gültiges Sync-Byte, also weg damit.
//Console.WriteLine("gse not in sync yet. sync byte = {0}", syncByte);
return;
}
}
else if (assemblyState == GseAssemblyState.NEED_MORE_DATA)
{
gseAssembler.Write(readBytes, 0, readBytes.Length);
return;
}
else if (assemblyState == GseAssemblyState.VALID)
{
gseAssembler.Position = 0;
AssembleGse(gseAssembler);
//Console.WriteLine("assembled {0} next sync byte is {1}", sucessfulAssembles++,syncByte);
if (sucessfulAssembles == 181)
{
}
gseAssembler = null;
HandleGseHem(ms);
return;
}
else if (assemblyState == GseAssemblyState.BROKEN_SUSPECT_ETHERTYPE)
{
gseAssembler = null;
HandleGseHem(ms);
return;
}
else if (assemblyState == GseAssemblyState.BROKEN_NEGATIVE_LENGTH)
{
gseAssembler = null;
HandleGseHem(ms);
return;
}
else
{
throw new NotImplementedException(string.Format("Unknown GSE assembler state: {0}, sync byte 0x{1}", assemblyState, syncByte));
}
}
private long sucessfulAssembles;
enum GseAssemblyState
{
NOT_YET_BEGUN,
VALID,
BROKEN_NEGATIVE_LENGTH,
NEED_MORE_DATA,
BROKEN_SUSPECT_LENGTH,
BROKEN_SUSPECT_ETHERTYPE,
VALID_NULL_PACKET, //somehow these only show up on TBS6903x, not DD Max SX8?
}
private GseAssemblyState ValidateGse(MemoryStream ms, bool tryFix = false)
{
if (ms == null)
return GseAssemblyState.NOT_YET_BEGUN;
long backupPosition = ms.Position;
ms.Position = 0;
while (ms.GetAvailableBytes() > 2)
{
long fixPosition = ms.Position;
//GSE-Header
byte byteA = ms.ReadUInt8();
bool startIndicator = (byteA & 0x80) != 0;
bool endIndicator = (byteA & 0x40) != 0;
int labelIndicator = (byteA & 0x30) >> 4;
if (!startIndicator && !endIndicator && labelIndicator == 0)
{
//end of base band frame
ms.Position = backupPosition;
return GseAssemblyState.VALID;
}
else
{
byte byteB = ms.ReadUInt8();
int gseLength = byteA & 0x0f;
gseLength <<= 8;
gseLength += byteB;
//Console.WriteLine("GSE Length: {0}", gseLength);
if (gseLength > 9000)
{
if (tryFix)
{
ms.Position = fixPosition;
ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes());
break;
}
ms.Position = backupPosition;
return GseAssemblyState.BROKEN_SUSPECT_LENGTH;
}
if (!startIndicator || !endIndicator)
{
byte fragId = ms.ReadUInt8();
gseLength--;
}
if (startIndicator && !endIndicator)
{
if (2 > ms.GetAvailableBytes())
{
ms.Position = backupPosition;
return GseAssemblyState.NEED_MORE_DATA;
}
ushort totalLength = ms.ReadUInt16BE();
gseLength -= 2;
}
if (startIndicator)
{
if (2 > ms.GetAvailableBytes())
{
ms.Position = backupPosition;
return GseAssemblyState.NEED_MORE_DATA;
}
ushort protocolType = ms.ReadUInt16BE();
if (!ValidateEthertype(protocolType))
{
if (tryFix)
{
ms.Position = fixPosition;
ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes());
break;
}
ms.Position = backupPosition;
return GseAssemblyState.BROKEN_SUSPECT_ETHERTYPE;
}
gseLength -= 2;
if (labelIndicator == 0)
{
if (6 > ms.GetAvailableBytes())
{
ms.Position = backupPosition;
return GseAssemblyState.NEED_MORE_DATA;
}
PhysicalAddress sixByteLabel = new PhysicalAddress(ms.ReadBytes(6));
gseLength -= 6;
}
else if (labelIndicator == 1)
{
byte[] threeByteLabel = ms.ReadBytes(3);
gseLength -= 3;
}
}
if (!startIndicator && endIndicator)
gseLength -= 4;
int startCrc32 = (int)ms.Position;
if (gseLength < 0)
{
if (tryFix)
{
ms.Position = fixPosition;
ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes());
break;
}
ms.Position = backupPosition;
return GseAssemblyState.BROKEN_NEGATIVE_LENGTH;
}
if (gseLength > ms.GetAvailableBytes())
{
ms.Position = backupPosition;
return GseAssemblyState.NEED_MORE_DATA;
}
ms.Position += gseLength;
if (!startIndicator && endIndicator)
{
if (4 > ms.GetAvailableBytes())
{
ms.Position = backupPosition;
return GseAssemblyState.NEED_MORE_DATA;
}
uint crc32 = ms.ReadUInt32BE();
int endCrc32 = (int)ms.Position;
DvbCrc32.ValidateCrc(ms, startCrc32, endCrc32);
}
}
}
ms.Position = backupPosition;
return GseAssemblyState.VALID;
}
private bool ValidateEthertype(ushort ethertype)
{
if (ethertype == 0x0800)
return true; //IPv4
if (ethertype == 0x86DD)
return true; //IPv6
else
return false; //There are many more valid values, but since we don't speak anything aside from IPv4/IPv6, we discard it.
}
private void AssembleGse(MemoryStream ms)
{
ushort protocolType = 0;
while (ms.GetAvailableBytes() > 2)
{
//GSE-Header
byte byteA = ms.ReadUInt8();
bool startIndicator = (byteA & 0x80) != 0;
bool endIndicator = (byteA & 0x40) != 0;
int labelIndicator = (byteA & 0x30) >> 4;
if (!startIndicator && !endIndicator && labelIndicator == 0)
{
//end of base band frame
return;
}
else
{
byte byteB = ms.ReadUInt8();
int gseLength = byteA & 0x0f;
gseLength <<= 8;
gseLength += byteB;
if (!startIndicator || !endIndicator)
{
byte fragId = ms.ReadUInt8();
gseLength--;
}
if (startIndicator && !endIndicator)
{
ushort totalLength = ms.ReadUInt16BE();
gseLength -= 2;
}
if (startIndicator)
{
protocolType = ms.ReadUInt16BE();
gseLength -= 2;
if (labelIndicator == 0)
{
PhysicalAddress sixByteLabel = new PhysicalAddress(ms.ReadBytes(6));
gseLength -= 6;
}
else if (labelIndicator == 1)
{
byte[] threeByteLabel = ms.ReadBytes(3);
gseLength -= 3;
}
}
if (!startIndicator && endIndicator)
gseLength -= 4;
int startCrc32 = (int)ms.Position;
byte[] payload = ms.ReadBytes(gseLength);
if (!startIndicator && endIndicator)
{
uint crc32 = ms.ReadUInt32BE();
int endCrc32 = (int)ms.Position;
DvbCrc32.ValidateCrc(ms, startCrc32, endCrc32);
}
switch (protocolType)
{
case 0x0800: //IPv4
MpeEventHandler.OnIpDatagram(PID, payload);
break;
default:
//throw new NotImplementedException(String.Format("Unknown GSE Protocol ID 0x{0:X4}", protocolType));
break;
}
}
}
}
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper5.Skyscraper
{
internal interface IGsEventHandler
{
void OnIpDatagram(int pid, byte[] payload);
void GsIpTrafficDetected();
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.GSE
{
internal class GseFragmentation
{
public void AddPacket(GsePacket packet)
{
if (spanningGsePackets == null)
{
spanningGsePackets = new List<GsePacket>();
TotalLength = packet.TotalLength.Value;
}
spanningGsePackets.Add(packet);
}
public ushort TotalLength { get; private set; }
private List<GsePacket> spanningGsePackets;
}
}

View File

@ -1,167 +0,0 @@
using skyscraper5.Skyscraper.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper;
namespace skyscraper8.GSE
{
internal class GsePacket : Validatable
{
public static GsePacket Read(MemoryStream ms)
{
GsePacket packet = new GsePacket();
byte byteA = ms.ReadUInt8();
packet.StartIndicator = (byteA & 0x80) != 0;
packet.EndIndicator = (byteA & 0x40) != 0;
packet.LabelIndicator = (byteA & 0x30) >> 4;
if (!packet.StartIndicator && !packet.EndIndicator && packet.LabelIndicator == 0)
{
//End of baseband frame
return packet;
}
else
{
byte byteB = ms.ReadUInt8();
int gseLength = byteA & 0x0f;
gseLength <<= 8;
gseLength += byteB;
if (!packet.StartIndicator || !packet.EndIndicator)
{
packet.FragmentId = ms.ReadUInt8();
gseLength--;
}
if (packet.StartIndicator && !packet.EndIndicator)
{
packet.TotalLength = ms.ReadUInt16BE();
gseLength -= 2;
}
if (packet.StartIndicator)
{
packet.ProtocolType = ms.ReadUInt16BE();
gseLength -= 2;
if (packet.LabelIndicator == 0)
{
packet.Label = new SixByteGseLabel(ms.ReadBytes(6));
gseLength -= 6;
}
else if (packet.LabelIndicator == 1)
{
packet.Label = new ThreeByteGseLabel(ms.ReadBytes(3));
gseLength -= 3;
}
}
if (!packet.StartIndicator && packet.EndIndicator)
gseLength -= 4;
if (gseLength < 0)
{
packet.Valid = false;
return packet;
}
if (gseLength > ms.GetAvailableBytes())
{
packet.Valid = false;
return packet;
}
packet.GseData = ms.ReadBytes(gseLength);
if (!packet.StartIndicator && packet.EndIndicator)
packet.Crc32 = ms.ReadUInt32BE();
}
packet.Valid = true;
return packet;
}
public uint? Crc32 { get; set; }
public byte[] GseData { get; set; }
public GseLabel Label { get; set; }
public ushort? ProtocolType { get; set; }
public ushort? TotalLength { get; set; }
public byte? FragmentId { get; set; }
public int LabelIndicator { get; private set; }
public bool EndIndicator { get; private set; }
public bool StartIndicator { get; private set; }
public bool IsPadding
{
get
{
return !StartIndicator && !EndIndicator && LabelIndicator == 0;
}
}
public bool IsCompletePacket
{
get
{
return StartIndicator && EndIndicator;
}
}
}
public abstract class GseLabel : Validatable
{
public abstract int LabelLength { get; }
public abstract string ToString();
}
public class ThreeByteGseLabel : GseLabel
{
public ThreeByteGseLabel(byte[] buffer)
{
if (buffer.Length != 3)
{
Valid = false;
return;
}
LabelBytes = buffer;
Valid = true;
}
public byte[] LabelBytes { get; private set; }
public override string ToString()
{
return BitConverter.ToString(LabelBytes);
}
public override int LabelLength => 3;
}
public class SixByteGseLabel : GseLabel
{
public SixByteGseLabel(byte[] buffer)
{
if (buffer.Length != 6)
{
Valid = false;
return;
}
MacAddress = new PhysicalAddress(buffer);
Valid = true;
}
public PhysicalAddress MacAddress { get; private set; }
public override string ToString()
{
return MacAddress.ToString();
}
public override int LabelLength => 6;
}
}

View File

@ -1,16 +0,0 @@
using skyscraper5.Skyscraper;
namespace skyscraper8.GSE;
public class NullGsEventHandler : IGsEventHandler
{
public void OnIpDatagram(int pid, byte[] payload)
{
}
public void GsIpTrafficDetected()
{
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Ieee802_1AB
{
internal interface ILldpFrameHandler
{
void OnLldpFrame(LldpFrame frame);
}
}

View File

@ -0,0 +1,154 @@
using System.Text;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Ieee802_1AB.Model;
namespace skyscraper8.Ieee802_1AB;
public class LldpFrame
{
public LldpFrame(byte[] contents)
{
MemoryStream ms = new MemoryStream(contents, false);
while (ms.Position < ms.Length)
{
ushort tlv = ms.ReadUInt16BE();
int type = (tlv & 0xfe00) >> 9;
int length = (tlv & 0x01ff);
byte[] value = ms.ReadBytes(length);
switch (type)
{
case 1: //Chassis
this.Chassis = new LldpChassis(value);
break;
case 2: //Port
this.Port = new LldpPort(value);
break;
case 3: //Time to Live
this.TimeToLive = (value[0] << 8) | value[1];
this.TimeToLive--;
break;
case 4:
this.PortDescription = Encoding.UTF8.GetString(value);
break;
case 5:
this.SystemName = Encoding.UTF8.GetString(value);
break;
case 6:
this.SystemDescription = Encoding.UTF8.GetString(value);
break;
case 7:
this.Capabilities = new LldpCapabilities(value);
break;
case 8:
this.ManagementAddress = new LldpManagementAddress(value);
break;
case 127:
string OrganizationUniqueCode = BitConverter.ToString(value, 0, 3);
byte OrganizationallyDefinedSubtype = value[3];
MemoryStream payload = new MemoryStream(value, 4, value.Length - 4);
switch (OrganizationUniqueCode)
{
case "00-80-C2": //IEEE 802.1 Chair
switch (OrganizationallyDefinedSubtype)
{
case 6: //VLAN ID
this.PortVlanIdentifier = ms.ReadUInt16BE();
break;
default:
throw new NotImplementedException(String.Format("Unknown IEEE 802.1 Subtype {0}! Please share a sample of this stream.",OrganizationallyDefinedSubtype));
}
break;
case "00-12-0F": //IEEE 802.3 Chair
switch (OrganizationallyDefinedSubtype)
{
//see 802.1AB-2009.pdf, Annex F
case 1: //MAC/PHY configuration/status TLV format
this.MacPhyConfigurationStatus = new LldpMacPhyConfigurationStatus(payload);
break;
default:
throw new NotImplementedException(String.Format("Unknown IEEE 802.3 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
}
break;
case "00-12-BB":
switch (OrganizationallyDefinedSubtype) //TR-41. Don't know which document yet.
{
case 1: //Media Capabilities
this.MediaCapabilities = new LldpMediaCapabilities(payload);
break;
default:
throw new NotImplementedException(String.Format("Unknown TR-41 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
}
break;
default:
throw new NotImplementedException(String.Format("Unknown Organization Unique Code {0}. Please share a sample of this stream.",OrganizationUniqueCode));
}
break;
case 0: //End of LLDP PDU
return;
default:
throw new NotImplementedException( String.Format("LLDP Option {0} not yet supported. Please share a sample of this stream.",type));
}
}
}
public LldpMediaCapabilities MediaCapabilities { get; set; }
public LldpMacPhyConfigurationStatus MacPhyConfigurationStatus { get; set; }
public ushort? PortVlanIdentifier { get; set; }
public LldpManagementAddress ManagementAddress { get; private set; }
public LldpCapabilities Capabilities { get; set; }
public string SystemDescription { get; set; }
public string SystemName { get; set; }
public string PortDescription { get; set; }
public int? TimeToLive { get; set; }
public LldpPort Port { get; set; }
public LldpChassis Chassis { get; private set; }
protected bool Equals(LldpFrame other)
{
return Port.Equals(other.Port) && Chassis.Equals(other.Chassis);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((LldpFrame)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Port, Chassis);
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
if (!string.IsNullOrEmpty(SystemName))
{
sb.Append(SystemName);
}
else
{
sb.Append(Chassis.ToString());
}
sb.Append(", ");
sb.Append(Port.ToString());
return sb.ToString();
}
}

View File

@ -0,0 +1,36 @@
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model;
public class LldpCapabilities
{
private readonly ushort capabilities;
private readonly ushort enabledCapabilities;
public LldpCapabilities(byte[] buffer)
{
MemoryStream ms = new MemoryStream(buffer);
this.capabilities = ms.ReadUInt16BE();
this.enabledCapabilities = ms.ReadUInt16BE();
}
public bool HasOther => (capabilities & 0x0001) != 0;
public bool HasRepeater => (capabilities & 0x0002) != 0;
public bool HasBridge => (capabilities & 0x0004) != 0;
public bool HasWlanAccessPoint => (capabilities & 0x0008) != 0;
public bool HasRouter => (capabilities & 0x0010) != 0;
public bool HasTelephone => (capabilities & 0x0020) != 0;
public bool HasDocsis => (capabilities & 0x0040) != 0;
public bool IsStation => (capabilities & 0x0080) != 0;
public bool HasCVLan => (capabilities & 0x0100) != 0;
public bool HasSVLan => (capabilities & 0x0200) != 0;
public bool HasTpmr => (capabilities & 0x0400) != 0;
public bool EnabledOther => (enabledCapabilities & 0x0001) != 0;
public bool EnabledRepeater => (enabledCapabilities & 0x0002) != 0;
public bool EnabledBridge => (enabledCapabilities & 0x0004) != 0;
public bool EnabledWlanAccessPoint => (enabledCapabilities & 0x0008) != 0;
public bool EnabledRouter => (enabledCapabilities & 0x0010) != 0;
public bool EnabledTelephone => (enabledCapabilities & 0x0020) != 0;
public bool EnabledDocsis => (enabledCapabilities & 0x0040) != 0;
}

View File

@ -0,0 +1,57 @@
using System.Net.NetworkInformation;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model;
public class LldpChassis
{
public LldpChassis(byte[] value)
{
MemoryStream ms = new MemoryStream(value);
ChassisSubtype = ms.ReadUInt8();
switch (ChassisSubtype)
{
case 4:
MacAddress = new PhysicalAddress(ms.ReadBytes(6));
break;
default:
throw new NotImplementedException(string.Format("Chassis Subtype {0} not yet supported. Please share a sample of this stream.", ChassisSubtype));
}
}
public PhysicalAddress MacAddress { get; set; }
public byte ChassisSubtype { get; private set; }
protected bool Equals(LldpChassis other)
{
return MacAddress.Equals(other.MacAddress) && ChassisSubtype == other.ChassisSubtype;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((LldpChassis)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(MacAddress, ChassisSubtype);
}
public override string ToString()
{
switch (ChassisSubtype)
{
case 4:
return MacAddress.ToString();
default:
return String.Format("Chassis Subtype {0}", ChassisSubtype);
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model
{
public class LldpMacPhyConfigurationStatus
{
private readonly byte autoNegotioationStatus;
private readonly ushort pmdAutoNegotiation;
public LldpMacPhyConfigurationStatus(MemoryStream ms)
{
autoNegotioationStatus = ms.ReadUInt8();
pmdAutoNegotiation = ms.ReadUInt16BE();
OperationalMauType = ms.ReadUInt16BE();
}
public ushort OperationalMauType { get; private set; }
public bool AutonegotiationSupported => (autoNegotioationStatus & 0x01) != 0;
public bool AutonegotiationStatus => (autoNegotioationStatus & 0x02) != 0;
}
}

View File

@ -0,0 +1,37 @@
using System.Net;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model;
public class LldpManagementAddress
{
public LldpManagementAddress(byte[] buffer)
{
MemoryStream ms = new MemoryStream(buffer, false);
byte managementAddressStringLength = ms.ReadUInt8();
ManagementAddressSubtype = ms.ReadUInt8();
managementAddressStringLength--;
if (ManagementAddressSubtype != 1)
{
throw new NotImplementedException("Unknown management address subtype. Please share a sample of this stream.");
}
ManagementAddress = new IPAddress(ms.ReadBytes(managementAddressStringLength));
InterfaceNumberingSubtype = ms.ReadUInt8();
InterfaceNumber = ms.ReadUInt32BE();
byte oidStringLength = ms.ReadUInt8();
ObjectIdentifier = ms.ReadBytes(oidStringLength);
}
public byte[] ObjectIdentifier { get; set; }
public uint InterfaceNumber { get; set; }
public byte InterfaceNumberingSubtype { get; set; }
public byte ManagementAddressSubtype { get; set; }
public IPAddress ManagementAddress { get; set; }
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model
{
public class LldpMediaCapabilities
{
private readonly ushort capabilities;
public byte NetworkConnectivity { get; set; }
public LldpMediaCapabilities(MemoryStream ms)
{
capabilities = ms.ReadUInt16BE();
NetworkConnectivity = ms.ReadUInt8();
}
public bool LldpMedCapable => (capabilities & 0x0001) != 0;
public bool NetworkPolicyCapable => (capabilities & 0x0002) != 0;
public bool LocationIdentificationCapable => (capabilities & 0x0004) != 0;
public bool ExtendedPowerViaMdiPse => (capabilities & 0x0008) != 0;
public bool ExtendedPowerViaMdiPd => (capabilities & 0x0010) != 0;
public bool InventoryCapable => (capabilities & 0x0020) != 0;
}
}

View File

@ -0,0 +1,79 @@
using System.Net.NetworkInformation;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ieee802_1AB.Model;
public class LldpPort
{
public LldpPort(byte[] buffer)
{
MemoryStream ms = new MemoryStream(buffer);
Subtype = ms.ReadUInt8();
switch (Subtype)
{
case 3:
MacAddress = new PhysicalAddress(ms.ReadBytes(6));
break;
case 5:
PortId = ms.ReadUTF8FixedLength((int)ms.GetAvailableBytes());
break;
default:
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
}
}
public PhysicalAddress MacAddress { get; set; }
public string PortId { get; set; }
public byte Subtype { get; private set; }
protected bool Equals(LldpPort other)
{
switch (Subtype)
{
case 3:
return MacAddress.Equals(other.MacAddress) && Subtype == other.Subtype;
case 5:
return PortId == other.PortId && Subtype == other.Subtype;
default:
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
}
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((LldpPort)obj);
}
public override int GetHashCode()
{
switch (Subtype)
{
case 3:
return HashCode.Combine(PortId, MacAddress);
case 5:
return HashCode.Combine(PortId, Subtype);
default:
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
}
}
public override string ToString()
{
switch (Subtype)
{
case 3:
return MacAddress.ToString();
case 5:
return PortId;
default:
return String.Format("Port Subtype {0}", Subtype);
}
}
}

Some files were not shown because too many files have changed in this diff Show More