client upload

This commit is contained in:
unknown 2022-02-03 11:05:24 -08:00
parent 1a30046915
commit db6a77d25e
282 changed files with 32766 additions and 0 deletions

1
deps/GSL vendored Submodule

@ -0,0 +1 @@
Subproject commit ebf0498363c53f0d3c403b0548212c147e3747fe

1
deps/WinToast vendored Submodule

@ -0,0 +1 @@
Subproject commit 5e441fd03543b999edb663caf8df7be37c0d575c

1
deps/asmjit vendored Submodule

@ -0,0 +1 @@
Subproject commit 2a706fd2ba355808cada31ac1eed8ce28caa6b37

1
deps/discord-rpc vendored Submodule

@ -0,0 +1 @@
Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33

5946
deps/extra/udis86/libudis86/itab.c vendored Normal file

File diff suppressed because it is too large Load Diff

939
deps/extra/udis86/libudis86/itab.h vendored Normal file
View File

@ -0,0 +1,939 @@
#ifndef UD_ITAB_H
#define UD_ITAB_H
/* itab.h -- generated by udis86:scripts/ud_itab.py, do no edit */
/* ud_table_type -- lookup table types (see decode.c) */
enum ud_table_type {
UD_TAB__OPC_VEX,
UD_TAB__OPC_TABLE,
UD_TAB__OPC_X87,
UD_TAB__OPC_MOD,
UD_TAB__OPC_RM,
UD_TAB__OPC_OSIZE,
UD_TAB__OPC_MODE,
UD_TAB__OPC_VEX_L,
UD_TAB__OPC_3DNOW,
UD_TAB__OPC_REG,
UD_TAB__OPC_ASIZE,
UD_TAB__OPC_VEX_W,
UD_TAB__OPC_SSE,
UD_TAB__OPC_VENDOR
};
/* ud_mnemonic -- mnemonic constants */
enum ud_mnemonic_code {
UD_Iaaa,
UD_Iaad,
UD_Iaam,
UD_Iaas,
UD_Iadc,
UD_Iadd,
UD_Iaddpd,
UD_Iaddps,
UD_Iaddsd,
UD_Iaddss,
UD_Iaddsubpd,
UD_Iaddsubps,
UD_Iaesdec,
UD_Iaesdeclast,
UD_Iaesenc,
UD_Iaesenclast,
UD_Iaesimc,
UD_Iaeskeygenassist,
UD_Iand,
UD_Iandnpd,
UD_Iandnps,
UD_Iandpd,
UD_Iandps,
UD_Iarpl,
UD_Iblendpd,
UD_Iblendps,
UD_Iblendvpd,
UD_Iblendvps,
UD_Ibound,
UD_Ibsf,
UD_Ibsr,
UD_Ibswap,
UD_Ibt,
UD_Ibtc,
UD_Ibtr,
UD_Ibts,
UD_Icall,
UD_Icbw,
UD_Icdq,
UD_Icdqe,
UD_Iclc,
UD_Icld,
UD_Iclflush,
UD_Iclgi,
UD_Icli,
UD_Iclts,
UD_Icmc,
UD_Icmova,
UD_Icmovae,
UD_Icmovb,
UD_Icmovbe,
UD_Icmovg,
UD_Icmovge,
UD_Icmovl,
UD_Icmovle,
UD_Icmovno,
UD_Icmovnp,
UD_Icmovns,
UD_Icmovnz,
UD_Icmovo,
UD_Icmovp,
UD_Icmovs,
UD_Icmovz,
UD_Icmp,
UD_Icmppd,
UD_Icmpps,
UD_Icmpsb,
UD_Icmpsd,
UD_Icmpsq,
UD_Icmpss,
UD_Icmpsw,
UD_Icmpxchg,
UD_Icmpxchg16b,
UD_Icmpxchg8b,
UD_Icomisd,
UD_Icomiss,
UD_Icpuid,
UD_Icqo,
UD_Icrc32,
UD_Icvtdq2pd,
UD_Icvtdq2ps,
UD_Icvtpd2dq,
UD_Icvtpd2pi,
UD_Icvtpd2ps,
UD_Icvtpi2pd,
UD_Icvtpi2ps,
UD_Icvtps2dq,
UD_Icvtps2pd,
UD_Icvtps2pi,
UD_Icvtsd2si,
UD_Icvtsd2ss,
UD_Icvtsi2sd,
UD_Icvtsi2ss,
UD_Icvtss2sd,
UD_Icvtss2si,
UD_Icvttpd2dq,
UD_Icvttpd2pi,
UD_Icvttps2dq,
UD_Icvttps2pi,
UD_Icvttsd2si,
UD_Icvttss2si,
UD_Icwd,
UD_Icwde,
UD_Idaa,
UD_Idas,
UD_Idec,
UD_Idiv,
UD_Idivpd,
UD_Idivps,
UD_Idivsd,
UD_Idivss,
UD_Idppd,
UD_Idpps,
UD_Iemms,
UD_Ienter,
UD_Iextractps,
UD_If2xm1,
UD_Ifabs,
UD_Ifadd,
UD_Ifaddp,
UD_Ifbld,
UD_Ifbstp,
UD_Ifchs,
UD_Ifclex,
UD_Ifcmovb,
UD_Ifcmovbe,
UD_Ifcmove,
UD_Ifcmovnb,
UD_Ifcmovnbe,
UD_Ifcmovne,
UD_Ifcmovnu,
UD_Ifcmovu,
UD_Ifcom,
UD_Ifcom2,
UD_Ifcomi,
UD_Ifcomip,
UD_Ifcomp,
UD_Ifcomp3,
UD_Ifcomp5,
UD_Ifcompp,
UD_Ifcos,
UD_Ifdecstp,
UD_Ifdiv,
UD_Ifdivp,
UD_Ifdivr,
UD_Ifdivrp,
UD_Ifemms,
UD_Iffree,
UD_Iffreep,
UD_Ifiadd,
UD_Ificom,
UD_Ificomp,
UD_Ifidiv,
UD_Ifidivr,
UD_Ifild,
UD_Ifimul,
UD_Ifincstp,
UD_Ifist,
UD_Ifistp,
UD_Ifisttp,
UD_Ifisub,
UD_Ifisubr,
UD_Ifld,
UD_Ifld1,
UD_Ifldcw,
UD_Ifldenv,
UD_Ifldl2e,
UD_Ifldl2t,
UD_Ifldlg2,
UD_Ifldln2,
UD_Ifldpi,
UD_Ifldz,
UD_Ifmul,
UD_Ifmulp,
UD_Ifndisi,
UD_Ifneni,
UD_Ifninit,
UD_Ifnop,
UD_Ifnsave,
UD_Ifnsetpm,
UD_Ifnstcw,
UD_Ifnstenv,
UD_Ifnstsw,
UD_Ifpatan,
UD_Ifprem,
UD_Ifprem1,
UD_Ifptan,
UD_Ifrndint,
UD_Ifrstor,
UD_Ifrstpm,
UD_Ifscale,
UD_Ifsin,
UD_Ifsincos,
UD_Ifsqrt,
UD_Ifst,
UD_Ifstp,
UD_Ifstp1,
UD_Ifstp8,
UD_Ifstp9,
UD_Ifsub,
UD_Ifsubp,
UD_Ifsubr,
UD_Ifsubrp,
UD_Iftst,
UD_Ifucom,
UD_Ifucomi,
UD_Ifucomip,
UD_Ifucomp,
UD_Ifucompp,
UD_Ifxam,
UD_Ifxch,
UD_Ifxch4,
UD_Ifxch7,
UD_Ifxrstor,
UD_Ifxsave,
UD_Ifxtract,
UD_Ifyl2x,
UD_Ifyl2xp1,
UD_Igetsec,
UD_Ihaddpd,
UD_Ihaddps,
UD_Ihlt,
UD_Ihsubpd,
UD_Ihsubps,
UD_Iidiv,
UD_Iimul,
UD_Iin,
UD_Iinc,
UD_Iinsb,
UD_Iinsd,
UD_Iinsertps,
UD_Iinsw,
UD_Iint,
UD_Iint1,
UD_Iint3,
UD_Iinto,
UD_Iinvd,
UD_Iinvept,
UD_Iinvlpg,
UD_Iinvlpga,
UD_Iinvvpid,
UD_Iiretd,
UD_Iiretq,
UD_Iiretw,
UD_Ija,
UD_Ijae,
UD_Ijb,
UD_Ijbe,
UD_Ijcxz,
UD_Ijecxz,
UD_Ijg,
UD_Ijge,
UD_Ijl,
UD_Ijle,
UD_Ijmp,
UD_Ijno,
UD_Ijnp,
UD_Ijns,
UD_Ijnz,
UD_Ijo,
UD_Ijp,
UD_Ijrcxz,
UD_Ijs,
UD_Ijz,
UD_Ilahf,
UD_Ilar,
UD_Ilddqu,
UD_Ildmxcsr,
UD_Ilds,
UD_Ilea,
UD_Ileave,
UD_Iles,
UD_Ilfence,
UD_Ilfs,
UD_Ilgdt,
UD_Ilgs,
UD_Ilidt,
UD_Illdt,
UD_Ilmsw,
UD_Ilock,
UD_Ilodsb,
UD_Ilodsd,
UD_Ilodsq,
UD_Ilodsw,
UD_Iloop,
UD_Iloope,
UD_Iloopne,
UD_Ilsl,
UD_Ilss,
UD_Iltr,
UD_Imaskmovdqu,
UD_Imaskmovq,
UD_Imaxpd,
UD_Imaxps,
UD_Imaxsd,
UD_Imaxss,
UD_Imfence,
UD_Iminpd,
UD_Iminps,
UD_Iminsd,
UD_Iminss,
UD_Imonitor,
UD_Imontmul,
UD_Imov,
UD_Imovapd,
UD_Imovaps,
UD_Imovbe,
UD_Imovd,
UD_Imovddup,
UD_Imovdq2q,
UD_Imovdqa,
UD_Imovdqu,
UD_Imovhlps,
UD_Imovhpd,
UD_Imovhps,
UD_Imovlhps,
UD_Imovlpd,
UD_Imovlps,
UD_Imovmskpd,
UD_Imovmskps,
UD_Imovntdq,
UD_Imovntdqa,
UD_Imovnti,
UD_Imovntpd,
UD_Imovntps,
UD_Imovntq,
UD_Imovq,
UD_Imovq2dq,
UD_Imovsb,
UD_Imovsd,
UD_Imovshdup,
UD_Imovsldup,
UD_Imovsq,
UD_Imovss,
UD_Imovsw,
UD_Imovsx,
UD_Imovsxd,
UD_Imovupd,
UD_Imovups,
UD_Imovzx,
UD_Impsadbw,
UD_Imul,
UD_Imulpd,
UD_Imulps,
UD_Imulsd,
UD_Imulss,
UD_Imwait,
UD_Ineg,
UD_Inop,
UD_Inot,
UD_Ior,
UD_Iorpd,
UD_Iorps,
UD_Iout,
UD_Ioutsb,
UD_Ioutsd,
UD_Ioutsw,
UD_Ipabsb,
UD_Ipabsd,
UD_Ipabsw,
UD_Ipackssdw,
UD_Ipacksswb,
UD_Ipackusdw,
UD_Ipackuswb,
UD_Ipaddb,
UD_Ipaddd,
UD_Ipaddq,
UD_Ipaddsb,
UD_Ipaddsw,
UD_Ipaddusb,
UD_Ipaddusw,
UD_Ipaddw,
UD_Ipalignr,
UD_Ipand,
UD_Ipandn,
UD_Ipavgb,
UD_Ipavgusb,
UD_Ipavgw,
UD_Ipblendvb,
UD_Ipblendw,
UD_Ipclmulqdq,
UD_Ipcmpeqb,
UD_Ipcmpeqd,
UD_Ipcmpeqq,
UD_Ipcmpeqw,
UD_Ipcmpestri,
UD_Ipcmpestrm,
UD_Ipcmpgtb,
UD_Ipcmpgtd,
UD_Ipcmpgtq,
UD_Ipcmpgtw,
UD_Ipcmpistri,
UD_Ipcmpistrm,
UD_Ipextrb,
UD_Ipextrd,
UD_Ipextrq,
UD_Ipextrw,
UD_Ipf2id,
UD_Ipf2iw,
UD_Ipfacc,
UD_Ipfadd,
UD_Ipfcmpeq,
UD_Ipfcmpge,
UD_Ipfcmpgt,
UD_Ipfmax,
UD_Ipfmin,
UD_Ipfmul,
UD_Ipfnacc,
UD_Ipfpnacc,
UD_Ipfrcp,
UD_Ipfrcpit1,
UD_Ipfrcpit2,
UD_Ipfrsqit1,
UD_Ipfrsqrt,
UD_Ipfsub,
UD_Ipfsubr,
UD_Iphaddd,
UD_Iphaddsw,
UD_Iphaddw,
UD_Iphminposuw,
UD_Iphsubd,
UD_Iphsubsw,
UD_Iphsubw,
UD_Ipi2fd,
UD_Ipi2fw,
UD_Ipinsrb,
UD_Ipinsrd,
UD_Ipinsrq,
UD_Ipinsrw,
UD_Ipmaddubsw,
UD_Ipmaddwd,
UD_Ipmaxsb,
UD_Ipmaxsd,
UD_Ipmaxsw,
UD_Ipmaxub,
UD_Ipmaxud,
UD_Ipmaxuw,
UD_Ipminsb,
UD_Ipminsd,
UD_Ipminsw,
UD_Ipminub,
UD_Ipminud,
UD_Ipminuw,
UD_Ipmovmskb,
UD_Ipmovsxbd,
UD_Ipmovsxbq,
UD_Ipmovsxbw,
UD_Ipmovsxdq,
UD_Ipmovsxwd,
UD_Ipmovsxwq,
UD_Ipmovzxbd,
UD_Ipmovzxbq,
UD_Ipmovzxbw,
UD_Ipmovzxdq,
UD_Ipmovzxwd,
UD_Ipmovzxwq,
UD_Ipmuldq,
UD_Ipmulhrsw,
UD_Ipmulhrw,
UD_Ipmulhuw,
UD_Ipmulhw,
UD_Ipmulld,
UD_Ipmullw,
UD_Ipmuludq,
UD_Ipop,
UD_Ipopa,
UD_Ipopad,
UD_Ipopcnt,
UD_Ipopfd,
UD_Ipopfq,
UD_Ipopfw,
UD_Ipor,
UD_Iprefetch,
UD_Iprefetchnta,
UD_Iprefetcht0,
UD_Iprefetcht1,
UD_Iprefetcht2,
UD_Ipsadbw,
UD_Ipshufb,
UD_Ipshufd,
UD_Ipshufhw,
UD_Ipshuflw,
UD_Ipshufw,
UD_Ipsignb,
UD_Ipsignd,
UD_Ipsignw,
UD_Ipslld,
UD_Ipslldq,
UD_Ipsllq,
UD_Ipsllw,
UD_Ipsrad,
UD_Ipsraw,
UD_Ipsrld,
UD_Ipsrldq,
UD_Ipsrlq,
UD_Ipsrlw,
UD_Ipsubb,
UD_Ipsubd,
UD_Ipsubq,
UD_Ipsubsb,
UD_Ipsubsw,
UD_Ipsubusb,
UD_Ipsubusw,
UD_Ipsubw,
UD_Ipswapd,
UD_Iptest,
UD_Ipunpckhbw,
UD_Ipunpckhdq,
UD_Ipunpckhqdq,
UD_Ipunpckhwd,
UD_Ipunpcklbw,
UD_Ipunpckldq,
UD_Ipunpcklqdq,
UD_Ipunpcklwd,
UD_Ipush,
UD_Ipusha,
UD_Ipushad,
UD_Ipushfd,
UD_Ipushfq,
UD_Ipushfw,
UD_Ipxor,
UD_Ircl,
UD_Ircpps,
UD_Ircpss,
UD_Ircr,
UD_Irdmsr,
UD_Irdpmc,
UD_Irdrand,
UD_Irdtsc,
UD_Irdtscp,
UD_Irep,
UD_Irepne,
UD_Iret,
UD_Iretf,
UD_Irol,
UD_Iror,
UD_Iroundpd,
UD_Iroundps,
UD_Iroundsd,
UD_Iroundss,
UD_Irsm,
UD_Irsqrtps,
UD_Irsqrtss,
UD_Isahf,
UD_Isalc,
UD_Isar,
UD_Isbb,
UD_Iscasb,
UD_Iscasd,
UD_Iscasq,
UD_Iscasw,
UD_Iseta,
UD_Isetae,
UD_Isetb,
UD_Isetbe,
UD_Isetg,
UD_Isetge,
UD_Isetl,
UD_Isetle,
UD_Isetno,
UD_Isetnp,
UD_Isetns,
UD_Isetnz,
UD_Iseto,
UD_Isetp,
UD_Isets,
UD_Isetz,
UD_Isfence,
UD_Isgdt,
UD_Ishl,
UD_Ishld,
UD_Ishr,
UD_Ishrd,
UD_Ishufpd,
UD_Ishufps,
UD_Isidt,
UD_Iskinit,
UD_Isldt,
UD_Ismsw,
UD_Isqrtpd,
UD_Isqrtps,
UD_Isqrtsd,
UD_Isqrtss,
UD_Istc,
UD_Istd,
UD_Istgi,
UD_Isti,
UD_Istmxcsr,
UD_Istosb,
UD_Istosd,
UD_Istosq,
UD_Istosw,
UD_Istr,
UD_Isub,
UD_Isubpd,
UD_Isubps,
UD_Isubsd,
UD_Isubss,
UD_Iswapgs,
UD_Isyscall,
UD_Isysenter,
UD_Isysexit,
UD_Isysret,
UD_Itest,
UD_Iucomisd,
UD_Iucomiss,
UD_Iud2,
UD_Iunpckhpd,
UD_Iunpckhps,
UD_Iunpcklpd,
UD_Iunpcklps,
UD_Ivaddpd,
UD_Ivaddps,
UD_Ivaddsd,
UD_Ivaddss,
UD_Ivaddsubpd,
UD_Ivaddsubps,
UD_Ivaesdec,
UD_Ivaesdeclast,
UD_Ivaesenc,
UD_Ivaesenclast,
UD_Ivaesimc,
UD_Ivaeskeygenassist,
UD_Ivandnpd,
UD_Ivandnps,
UD_Ivandpd,
UD_Ivandps,
UD_Ivblendpd,
UD_Ivblendps,
UD_Ivblendvpd,
UD_Ivblendvps,
UD_Ivbroadcastsd,
UD_Ivbroadcastss,
UD_Ivcmppd,
UD_Ivcmpps,
UD_Ivcmpsd,
UD_Ivcmpss,
UD_Ivcomisd,
UD_Ivcomiss,
UD_Ivcvtdq2pd,
UD_Ivcvtdq2ps,
UD_Ivcvtpd2dq,
UD_Ivcvtpd2ps,
UD_Ivcvtps2dq,
UD_Ivcvtps2pd,
UD_Ivcvtsd2si,
UD_Ivcvtsd2ss,
UD_Ivcvtsi2sd,
UD_Ivcvtsi2ss,
UD_Ivcvtss2sd,
UD_Ivcvtss2si,
UD_Ivcvttpd2dq,
UD_Ivcvttps2dq,
UD_Ivcvttsd2si,
UD_Ivcvttss2si,
UD_Ivdivpd,
UD_Ivdivps,
UD_Ivdivsd,
UD_Ivdivss,
UD_Ivdppd,
UD_Ivdpps,
UD_Iverr,
UD_Iverw,
UD_Ivextractf128,
UD_Ivextractps,
UD_Ivhaddpd,
UD_Ivhaddps,
UD_Ivhsubpd,
UD_Ivhsubps,
UD_Ivinsertf128,
UD_Ivinsertps,
UD_Ivlddqu,
UD_Ivmaskmovdqu,
UD_Ivmaskmovpd,
UD_Ivmaskmovps,
UD_Ivmaxpd,
UD_Ivmaxps,
UD_Ivmaxsd,
UD_Ivmaxss,
UD_Ivmcall,
UD_Ivmclear,
UD_Ivminpd,
UD_Ivminps,
UD_Ivminsd,
UD_Ivminss,
UD_Ivmlaunch,
UD_Ivmload,
UD_Ivmmcall,
UD_Ivmovapd,
UD_Ivmovaps,
UD_Ivmovd,
UD_Ivmovddup,
UD_Ivmovdqa,
UD_Ivmovdqu,
UD_Ivmovhlps,
UD_Ivmovhpd,
UD_Ivmovhps,
UD_Ivmovlhps,
UD_Ivmovlpd,
UD_Ivmovlps,
UD_Ivmovmskpd,
UD_Ivmovmskps,
UD_Ivmovntdq,
UD_Ivmovntdqa,
UD_Ivmovntpd,
UD_Ivmovntps,
UD_Ivmovq,
UD_Ivmovsd,
UD_Ivmovshdup,
UD_Ivmovsldup,
UD_Ivmovss,
UD_Ivmovupd,
UD_Ivmovups,
UD_Ivmpsadbw,
UD_Ivmptrld,
UD_Ivmptrst,
UD_Ivmread,
UD_Ivmresume,
UD_Ivmrun,
UD_Ivmsave,
UD_Ivmulpd,
UD_Ivmulps,
UD_Ivmulsd,
UD_Ivmulss,
UD_Ivmwrite,
UD_Ivmxoff,
UD_Ivmxon,
UD_Ivorpd,
UD_Ivorps,
UD_Ivpabsb,
UD_Ivpabsd,
UD_Ivpabsw,
UD_Ivpackssdw,
UD_Ivpacksswb,
UD_Ivpackusdw,
UD_Ivpackuswb,
UD_Ivpaddb,
UD_Ivpaddd,
UD_Ivpaddq,
UD_Ivpaddsb,
UD_Ivpaddsw,
UD_Ivpaddusb,
UD_Ivpaddusw,
UD_Ivpaddw,
UD_Ivpalignr,
UD_Ivpand,
UD_Ivpandn,
UD_Ivpavgb,
UD_Ivpavgw,
UD_Ivpblendvb,
UD_Ivpblendw,
UD_Ivpclmulqdq,
UD_Ivpcmpeqb,
UD_Ivpcmpeqd,
UD_Ivpcmpeqq,
UD_Ivpcmpeqw,
UD_Ivpcmpestri,
UD_Ivpcmpestrm,
UD_Ivpcmpgtb,
UD_Ivpcmpgtd,
UD_Ivpcmpgtq,
UD_Ivpcmpgtw,
UD_Ivpcmpistri,
UD_Ivpcmpistrm,
UD_Ivperm2f128,
UD_Ivpermilpd,
UD_Ivpermilps,
UD_Ivpextrb,
UD_Ivpextrd,
UD_Ivpextrq,
UD_Ivpextrw,
UD_Ivphaddd,
UD_Ivphaddsw,
UD_Ivphaddw,
UD_Ivphminposuw,
UD_Ivphsubd,
UD_Ivphsubsw,
UD_Ivphsubw,
UD_Ivpinsrb,
UD_Ivpinsrd,
UD_Ivpinsrq,
UD_Ivpinsrw,
UD_Ivpmaddubsw,
UD_Ivpmaddwd,
UD_Ivpmaxsb,
UD_Ivpmaxsd,
UD_Ivpmaxsw,
UD_Ivpmaxub,
UD_Ivpmaxud,
UD_Ivpmaxuw,
UD_Ivpminsb,
UD_Ivpminsd,
UD_Ivpminsw,
UD_Ivpminub,
UD_Ivpminud,
UD_Ivpminuw,
UD_Ivpmovmskb,
UD_Ivpmovsxbd,
UD_Ivpmovsxbq,
UD_Ivpmovsxbw,
UD_Ivpmovsxwd,
UD_Ivpmovsxwq,
UD_Ivpmovzxbd,
UD_Ivpmovzxbq,
UD_Ivpmovzxbw,
UD_Ivpmovzxdq,
UD_Ivpmovzxwd,
UD_Ivpmovzxwq,
UD_Ivpmuldq,
UD_Ivpmulhrsw,
UD_Ivpmulhuw,
UD_Ivpmulhw,
UD_Ivpmulld,
UD_Ivpmullw,
UD_Ivpor,
UD_Ivpsadbw,
UD_Ivpshufb,
UD_Ivpshufd,
UD_Ivpshufhw,
UD_Ivpshuflw,
UD_Ivpsignb,
UD_Ivpsignd,
UD_Ivpsignw,
UD_Ivpslld,
UD_Ivpslldq,
UD_Ivpsllq,
UD_Ivpsllw,
UD_Ivpsrad,
UD_Ivpsraw,
UD_Ivpsrld,
UD_Ivpsrldq,
UD_Ivpsrlq,
UD_Ivpsrlw,
UD_Ivpsubb,
UD_Ivpsubd,
UD_Ivpsubq,
UD_Ivpsubsb,
UD_Ivpsubsw,
UD_Ivpsubusb,
UD_Ivpsubusw,
UD_Ivpsubw,
UD_Ivptest,
UD_Ivpunpckhbw,
UD_Ivpunpckhdq,
UD_Ivpunpckhqdq,
UD_Ivpunpckhwd,
UD_Ivpunpcklbw,
UD_Ivpunpckldq,
UD_Ivpunpcklqdq,
UD_Ivpunpcklwd,
UD_Ivpxor,
UD_Ivrcpps,
UD_Ivrcpss,
UD_Ivroundpd,
UD_Ivroundps,
UD_Ivroundsd,
UD_Ivroundss,
UD_Ivrsqrtps,
UD_Ivrsqrtss,
UD_Ivshufpd,
UD_Ivshufps,
UD_Ivsqrtpd,
UD_Ivsqrtps,
UD_Ivsqrtsd,
UD_Ivsqrtss,
UD_Ivstmxcsr,
UD_Ivsubpd,
UD_Ivsubps,
UD_Ivsubsd,
UD_Ivsubss,
UD_Ivtestpd,
UD_Ivtestps,
UD_Ivucomisd,
UD_Ivucomiss,
UD_Ivunpckhpd,
UD_Ivunpckhps,
UD_Ivunpcklpd,
UD_Ivunpcklps,
UD_Ivxorpd,
UD_Ivxorps,
UD_Ivzeroall,
UD_Ivzeroupper,
UD_Iwait,
UD_Iwbinvd,
UD_Iwrmsr,
UD_Ixadd,
UD_Ixchg,
UD_Ixcryptcbc,
UD_Ixcryptcfb,
UD_Ixcryptctr,
UD_Ixcryptecb,
UD_Ixcryptofb,
UD_Ixgetbv,
UD_Ixlatb,
UD_Ixor,
UD_Ixorpd,
UD_Ixorps,
UD_Ixrstor,
UD_Ixsave,
UD_Ixsetbv,
UD_Ixsha1,
UD_Ixsha256,
UD_Ixstore,
UD_Iinvalid,
UD_I3dnow,
UD_Inone,
UD_Idb,
UD_Ipause,
UD_MAX_MNEMONIC_CODE
};
extern const char * ud_mnemonics_str[];
#endif /* UD_ITAB_H */

1
deps/libtomcrypt vendored Submodule

@ -0,0 +1 @@
Subproject commit 673f5ce29015a9bba3c96792920a10601b5b0718

1
deps/libtommath vendored Submodule

@ -0,0 +1 @@
Subproject commit bea9270646303baf683f4ba2ddf0d70721f0e55d

1
deps/lua vendored Submodule

@ -0,0 +1 @@
Subproject commit 5d708c3f9cae12820e415d4f89c9eacbe2ab964b

1
deps/minhook vendored Submodule

@ -0,0 +1 @@
Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18

34
deps/premake/asmjit.lua vendored Normal file
View File

@ -0,0 +1,34 @@
asmjit = {
source = path.join(dependencies.basePath, "asmjit"),
}
function asmjit.import()
links { "asmjit" }
asmjit.includes()
end
function asmjit.includes()
includedirs {
path.join(asmjit.source, "src")
}
defines {
"ASMJIT_STATIC"
}
end
function asmjit.project()
project "asmjit"
language "C++"
asmjit.includes()
files {
path.join(asmjit.source, "src/**.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, asmjit)

39
deps/premake/discord-rpc.lua vendored Normal file
View File

@ -0,0 +1,39 @@
discordrpc = {
source = path.join(dependencies.basePath, "discord-rpc"),
}
function discordrpc.import()
links { "discord-rpc" }
discordrpc.includes()
end
function discordrpc.includes()
includedirs {
path.join(discordrpc.source, "include"),
}
end
function discordrpc.project()
project "discord-rpc"
language "C++"
discordrpc.includes()
rapidjson.import();
files {
path.join(discordrpc.source, "src/*.h"),
path.join(discordrpc.source, "src/*.cpp"),
}
removefiles {
path.join(discordrpc.source, "src/dllmain.cpp"),
path.join(discordrpc.source, "src/*_linux.cpp"),
path.join(discordrpc.source, "src/*_unix.cpp"),
path.join(discordrpc.source, "src/*_osx.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, discordrpc)

19
deps/premake/gsl.lua vendored Normal file
View File

@ -0,0 +1,19 @@
gsl = {
source = path.join(dependencies.basePath, "GSL"),
}
function gsl.import()
gsl.includes()
end
function gsl.includes()
includedirs {
path.join(gsl.source, "include")
}
end
function gsl.project()
end
table.insert(dependencies, gsl)

37
deps/premake/l_u_a.lua vendored Normal file
View File

@ -0,0 +1,37 @@
-- Scripts or variables named lua fuck with premake ._.
l_u_a = {
source = path.join(dependencies.basePath, "lua"),
}
function l_u_a.import()
links { "lua" }
l_u_a.includes()
end
function l_u_a.includes()
includedirs {
l_u_a.source
}
end
function l_u_a.project()
project "lua"
language "C"
l_u_a.includes()
files {
path.join(l_u_a.source, "*.h"),
path.join(l_u_a.source, "*.c"),
}
removefiles {
path.join(l_u_a.source, "onelua.c"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, l_u_a)

61
deps/premake/libtomcrypt.lua vendored Normal file
View File

@ -0,0 +1,61 @@
libtomcrypt = {
source = path.join(dependencies.basePath, "libtomcrypt"),
}
function libtomcrypt.import()
links {
"libtomcrypt"
}
libtomcrypt.includes()
end
function libtomcrypt.includes()
includedirs {
path.join(libtomcrypt.source, "src/headers")
}
defines {
"LTC_NO_FAST",
"LTC_NO_PROTOTYPES",
"LTC_NO_RSA_BLINDING",
}
end
function libtomcrypt.project()
project "libtomcrypt"
language "C"
libtomcrypt.includes()
libtommath.import()
files {
path.join(libtomcrypt.source, "src/**.c"),
}
removefiles {
path.join(libtomcrypt.source, "src/**/*tab.c"),
path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"),
}
defines {
"_CRT_SECURE_NO_WARNINGS",
"LTC_SOURCE",
"_LIB",
"USE_LTM"
}
removedefines {
"_DLL",
"_USRDLL"
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, libtomcrypt)

52
deps/premake/libtommath.lua vendored Normal file
View File

@ -0,0 +1,52 @@
libtommath = {
source = path.join(dependencies.basePath, "libtommath"),
}
function libtommath.import()
links {
"libtommath"
}
libtommath.includes()
end
function libtommath.includes()
includedirs {
libtommath.source
}
defines {
"LTM_DESC",
"__STDC_IEC_559__",
"MP_NO_DEV_URANDOM",
}
end
function libtommath.project()
project "libtommath"
language "C"
libtommath.includes()
files {
path.join(libtommath.source, "*.c"),
}
defines {
"_LIB"
}
removedefines {
"_DLL",
"_USRDLL"
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, libtommath)

31
deps/premake/minhook.lua vendored Normal file
View File

@ -0,0 +1,31 @@
minhook = {
source = path.join(dependencies.basePath, "minhook"),
}
function minhook.import()
links { "minhook" }
minhook.includes()
end
function minhook.includes()
includedirs {
path.join(minhook.source, "include")
}
end
function minhook.project()
project "minhook"
language "C"
minhook.includes()
files {
path.join(minhook.source, "src/**.h"),
path.join(minhook.source, "src/**.c"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, minhook)

43
deps/premake/minizip.lua vendored Normal file
View File

@ -0,0 +1,43 @@
minizip = {
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
}
function minizip.import()
links { "minizip" }
zlib.import()
minizip.includes()
end
function minizip.includes()
includedirs {
minizip.source
}
zlib.includes()
end
function minizip.project()
project "minizip"
language "C"
minizip.includes()
files {
path.join(minizip.source, "*.h"),
path.join(minizip.source, "*.c"),
}
removefiles {
path.join(minizip.source, "miniunz.c"),
path.join(minizip.source, "minizip.c"),
}
defines {
"_CRT_SECURE_NO_DEPRECATE",
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, minizip)

60
deps/premake/protobuf.lua vendored Normal file
View File

@ -0,0 +1,60 @@
protobuf = {
source = path.join(dependencies.basePath, "protobuf"),
}
function protobuf.import()
links {
"protobuf"
}
protobuf.includes()
end
function protobuf.includes()
includedirs {
path.join(protobuf.source, "src"),
}
end
function protobuf.project()
project "protobuf"
language "C++"
protobuf.includes()
files {
path.join(protobuf.source, "src/**.cc"),
"./src/**.proto",
}
removefiles {
path.join(protobuf.source, "src/**/*test.cc"),
path.join(protobuf.source, "src/google/protobuf/*test*.cc"),
path.join(protobuf.source, "src/google/protobuf/testing/**.cc"),
path.join(protobuf.source, "src/google/protobuf/compiler/**.cc"),
path.join(protobuf.source, "src/google/protobuf/arena_nc.cc"),
path.join(protobuf.source, "src/google/protobuf/util/internal/error_listener.cc"),
path.join(protobuf.source, "**/*_gcc.cc"),
}
rules {
"ProtobufCompiler"
}
defines {
"_SCL_SECURE_NO_WARNINGS",
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
"_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS",
}
linkoptions {
"-IGNORE:4221"
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, protobuf)

19
deps/premake/rapidjson.lua vendored Normal file
View File

@ -0,0 +1,19 @@
rapidjson = {
source = path.join(dependencies.basePath, "rapidjson"),
}
function rapidjson.import()
rapidjson.includes()
end
function rapidjson.includes()
includedirs {
path.join(rapidjson.source, "include"),
}
end
function rapidjson.project()
end
table.insert(dependencies, rapidjson)

20
deps/premake/sol2.lua vendored Normal file
View File

@ -0,0 +1,20 @@
sol2 = {
source = path.join(dependencies.basePath, "sol2"),
}
function sol2.import()
sol2.includes()
l_u_a.import()
end
function sol2.includes()
includedirs {
path.join(sol2.source, "include")
}
end
function sol2.project()
end
table.insert(dependencies, sol2)

19
deps/premake/stb.lua vendored Normal file
View File

@ -0,0 +1,19 @@
stb = {
source = path.join(dependencies.basePath, "stb"),
}
function stb.import()
stb.includes()
end
function stb.includes()
includedirs {
stb.source
}
end
function stb.project()
end
table.insert(dependencies, stb)

37
deps/premake/udis86.lua vendored Normal file
View File

@ -0,0 +1,37 @@
udis86 = {
source = path.join(dependencies.basePath, "udis86"),
}
function udis86.import()
links {
"udis86"
}
udis86.includes()
end
function udis86.includes()
includedirs {
udis86.source,
path.join(udis86.source, "libudis86"),
path.join(dependencies.basePath, "extra/udis86"),
path.join(dependencies.basePath, "extra/udis86/libudis86"),
}
end
function udis86.project()
project "udis86"
language "C"
udis86.includes()
files {
path.join(udis86.source, "libudis86/*.c"),
path.join(dependencies.basePath, "extra/udis86/libudis86/*.c"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, udis86)

32
deps/premake/wintoast.lua vendored Normal file
View File

@ -0,0 +1,32 @@
wintoast = {
source = path.join(dependencies.basePath, "WinToast"),
}
function wintoast.import()
links { "WinToast" }
wintoast.includes()
end
function wintoast.includes()
includedirs {
path.join(wintoast.source, "src"),
}
end
function wintoast.project()
project "WinToast"
language "C++"
wintoast.includes()
rapidjson.import();
files {
path.join(wintoast.source, "src/*.h"),
path.join(wintoast.source, "src/*.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, wintoast)

39
deps/premake/zlib.lua vendored Normal file
View File

@ -0,0 +1,39 @@
zlib = {
source = path.join(dependencies.basePath, "zlib"),
}
function zlib.import()
links { "zlib" }
zlib.includes()
end
function zlib.includes()
includedirs {
zlib.source
}
defines {
"ZLIB_CONST",
}
end
function zlib.project()
project "zlib"
language "C"
zlib.includes()
files {
path.join(zlib.source, "*.h"),
path.join(zlib.source, "*.c"),
}
defines {
"_CRT_SECURE_NO_DEPRECATE",
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, zlib)

1
deps/protobuf vendored Submodule

@ -0,0 +1 @@
Subproject commit 5500c72c5b616da9f0125bcfab513987a1226e2b

1
deps/rapidjson vendored Submodule

@ -0,0 +1 @@
Subproject commit fd3dc29a5c2852df569e1ea81dbde2c412ac5051

1
deps/sol2 vendored Submodule

@ -0,0 +1 @@
Subproject commit 50b62c9346750b7c2c406c9e4c546f96b0bf021d

1
deps/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a

1
deps/udis86 vendored Submodule

@ -0,0 +1 @@
Subproject commit 56ff6c87c11de0ffa725b14339004820556e343d

1
deps/zlib vendored Submodule

@ -0,0 +1 @@
Subproject commit 2014a993addbc8f1b9785d97f55fd189792c2f78

3
generate.bat Normal file
View File

@ -0,0 +1,3 @@
@echo off
git submodule update --init --recursive
tools\premake5 %* vs2019

365
premake5.lua Normal file
View File

@ -0,0 +1,365 @@
gitVersioningCommand = "git describe --tags --dirty --always"
gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"
-- Quote the given string input as a C string
function cstrquote(value)
if value == nil then
return "\"\""
end
result = value:gsub("\\", "\\\\")
result = result:gsub("\"", "\\\"")
result = result:gsub("\n", "\\n")
result = result:gsub("\t", "\\t")
result = result:gsub("\r", "\\r")
result = result:gsub("\a", "\\a")
result = result:gsub("\b", "\\b")
result = "\"" .. result .. "\""
return result
end
-- Converts tags in "vX.X.X" format and given revision number Y to an array of numbers {X,X,X,Y}.
-- In the case where the format does not work fall back to padding with zeroes and just ending with the revision number.
-- partscount can be either 3 or 4.
function vertonumarr(value, vernumber, partscount)
vernum = {}
for num in string.gmatch(value or "", "%d+") do
if #vernum < 3 then
table.insert(vernum, tonumber(num))
end
end
while #vernum < 3 do
table.insert(vernum, 0)
end
if #vernum < partscount then
table.insert(vernum, tonumber(vernumber))
end
return vernum
end
dependencies = {
basePath = "./deps"
}
function dependencies.load()
dir = path.join(dependencies.basePath, "premake/*.lua")
deps = os.matchfiles(dir)
for i, dep in pairs(deps) do
dep = dep:gsub(".lua", "")
require(dep)
end
end
function dependencies.imports()
for i, proj in pairs(dependencies) do
if type(i) == 'number' then
proj.import()
end
end
end
function dependencies.projects()
for i, proj in pairs(dependencies) do
if type(i) == 'number' then
proj.project()
end
end
end
newoption {
trigger = "copy-to",
description = "Optional, copy the EXE to a custom folder after build, define the path here if wanted.",
value = "PATH"
}
newoption {
trigger = "dev-build",
description = "Enable development builds of the client."
}
newaction {
trigger = "version",
description = "Returns the version string for the current commit of the source code.",
onWorkspace = function(wks)
-- get current version via git
local proc = assert(io.popen(gitVersioningCommand, "r"))
local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "")
proc:close()
local version = gitDescribeOutput
proc = assert(io.popen(gitCurrentBranchCommand, "r"))
local gitCurrentBranchOutput = assert(proc:read('*a')):gsub("%s+", "")
local gitCurrentBranchSuccess = proc:close()
if gitCurrentBranchSuccess then
-- We got a branch name, check if it is a feature branch
if gitCurrentBranchOutput ~= "develop" and gitCurrentBranchOutput ~= "master" then
version = version .. "-" .. gitCurrentBranchOutput
end
end
print(version)
os.exit(0)
end
}
newaction {
trigger = "generate-buildinfo",
description = "Sets up build information file like version.h.",
onWorkspace = function(wks)
-- get old version number from version.hpp if any
local oldVersion = "(none)"
local oldVersionHeader = io.open(wks.location .. "/src/version.h", "r")
if oldVersionHeader ~= nil then
local oldVersionHeaderContent = assert(oldVersionHeader:read('*l'))
while oldVersionHeaderContent do
m = string.match(oldVersionHeaderContent, "#define GIT_DESCRIBE (.+)%s*$")
if m ~= nil then
oldVersion = m
end
oldVersionHeaderContent = oldVersionHeader:read('*l')
end
end
-- get current version via git
local proc = assert(io.popen(gitVersioningCommand, "r"))
local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "")
proc:close()
-- generate version.hpp with a revision number if not equal
gitDescribeOutputQuoted = cstrquote(gitDescribeOutput)
if oldVersion ~= gitDescribeOutputQuoted then
-- get current git hash and write to version.txt (used by the preliminary updater)
-- TODO - remove once proper updater and release versioning exists
local proc = assert(io.popen("git rev-parse HEAD", "r"))
local gitCommitHash = assert(proc:read('*a')):gsub("%s+", "")
proc:close()
-- get whether this is a clean revision (no uncommitted changes)
proc = assert(io.popen("git status --porcelain", "r"))
local revDirty = (assert(proc:read('*a')) ~= "")
if revDirty then revDirty = 1 else revDirty = 0 end
proc:close()
-- get current tag name
proc = assert(io.popen("git describe --tags --abbrev=0"))
local tagName = proc:read('*l')
-- get current branch name
proc = assert(io.popen("git branch --show-current"))
local branchName = proc:read('*l')
-- branch for ci
if branchName == nil or branchName == '' then
proc = assert(io.popen("git show -s --pretty=%d HEAD"))
local branchInfo = proc:read('*l')
m = string.match(branchInfo, ".+,.+, ([^)]+)")
if m ~= nil then
branchName = m
end
end
if branchName == nil then
branchName = "develop"
end
print("Detected branch: " .. branchName)
-- get revision number via git
local proc = assert(io.popen("git rev-list --count HEAD", "r"))
local revNumber = assert(proc:read('*a')):gsub("%s+", "")
print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted)
-- write to version.txt for preliminary updater
-- NOTE - remove this once we have a proper updater and proper release versioning
local versionFile = assert(io.open(wks.location .. "/version.txt", "w"))
versionFile:write(gitCommitHash)
versionFile:close()
-- write version header
local versionHeader = assert(io.open(wks.location .. "/src/version.h", "w"))
versionHeader:write("/*\n")
versionHeader:write(" * Automatically generated by premake5.\n")
versionHeader:write(" * Do not touch!\n")
versionHeader:write(" */\n")
versionHeader:write("\n")
versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n")
versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n")
versionHeader:write("#define GIT_HASH " .. cstrquote(gitCommitHash) .. "\n")
versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n")
versionHeader:write("#define GIT_BRANCH " .. cstrquote(branchName) .. "\n")
versionHeader:write("\n")
versionHeader:write("// Version transformed for RC files\n")
versionHeader:write("#define VERSION_PRODUCT_RC " .. table.concat(vertonumarr(tagName, revNumber, 3), ",") .. "\n")
versionHeader:write("#define VERSION_PRODUCT " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 3), ".")) .. "\n")
versionHeader:write("#define VERSION_FILE_RC " .. table.concat(vertonumarr(tagName, revNumber, 4), ",") .. "\n")
versionHeader:write("#define VERSION_FILE " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 4), ".")) .. "\n")
versionHeader:write("\n")
versionHeader:write("// Alias definitions\n")
versionHeader:write("#define VERSION GIT_DESCRIBE\n")
versionHeader:write("#define SHORTVERSION VERSION_PRODUCT\n")
versionHeader:close()
local versionHeader = assert(io.open(wks.location .. "/src/version.hpp", "w"))
versionHeader:write("/*\n")
versionHeader:write(" * Automatically generated by premake5.\n")
versionHeader:write(" * Do not touch!\n")
versionHeader:write(" *\n")
versionHeader:write(" * This file exists for reasons of complying with our coding standards.\n")
versionHeader:write(" *\n")
versionHeader:write(" * The Resource Compiler will ignore any content from C++ header files if they're not from STDInclude.hpp.\n")
versionHeader:write(" * That's the reason why we now place all version info in version.h instead.\n")
versionHeader:write(" */\n")
versionHeader:write("\n")
versionHeader:write("#include \".\\version.h\"\n")
versionHeader:close()
end
end
}
dependencies.load()
workspace "h1-mod"
startproject "client"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
configurations {"Debug", "Release"}
architecture "x64"
platforms "x64"
buildoptions "/std:c++latest"
systemversion "latest"
symbols "On"
staticruntime "On"
editandcontinue "Off"
warnings "Extra"
characterset "ASCII"
if _OPTIONS["dev-build"] then
defines {"DEV_BUILD"}
end
if os.getenv("CI") then
defines {"CI"}
end
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
configuration "windows"
defines {"_WINDOWS", "WIN32"}
configuration "Release"
optimize "Size"
buildoptions {"/GL"}
linkoptions { "/IGNORE:4702", "/LTCG" }
defines {"NDEBUG"}
flags {"FatalCompileWarnings"}
configuration "Debug"
optimize "Debug"
defines {"DEBUG", "_DEBUG"}
configuration {}
project "common"
kind "StaticLib"
language "C++"
files {"./src/common/**.hpp", "./src/common/**.cpp"}
includedirs {"./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
dependencies.imports()
project "runner"
kind "WindowedApp"
language "C++"
files {"./src/runner/**.rc", "./src/runner/**.hpp", "./src/runner/**.cpp", "./src/runner/resources/**.*"}
includedirs {"./src/runner", "./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
links {"common"}
dependencies.imports()
project "client"
kind "ConsoleApp"
language "C++"
targetname "h1-mod"
pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp"
linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/LAST:.main", "/PDBCompress"}
files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"}
includedirs {"./src/client", "./src/common", "%{prj.location}/src"}
resincludedirs {"$(ProjectDir)src"}
dependson {"tlsdll", "runner"}
links {"common"}
prebuildcommands {"pushd %{_MAIN_SCRIPT_DIR}", "tools\\premake5 generate-buildinfo", "popd"}
if _OPTIONS["copy-to"] then
postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""}
end
dependencies.imports()
project "tlsdll"
kind "SharedLib"
language "C++"
files {"./src/tlsdll/**.rc", "./src/tlsdll/**.hpp", "./src/tlsdll/**.cpp", "./src/tlsdll/resources/**.*"}
includedirs {"./src/tlsdll", "%{prj.location}/src"}
links {"common"}
resincludedirs {"$(ProjectDir)src"}
project "runner"
kind "WindowedApp"
language "C++"
files {"./src/runner/**.rc", "./src/runner/**.hpp", "./src/runner/**.cpp", "./src/runner/resources/**.*"}
includedirs {"./src/runner", "./src/common", "%{prj.location}/src"}
links {"common"}
resincludedirs {"$(ProjectDir)src"}
links {"common"}
dependencies.imports()
group "Dependencies"
dependencies.projects()
rule "ProtobufCompiler"
display "Protobuf compiler"
location "./build"
fileExtension ".proto"
buildmessage "Compiling %(Identity) with protoc..."
buildcommands {'@echo off', 'path "$(SolutionDir)\\..\\tools"',
'if not exist "$(ProjectDir)\\src\\proto" mkdir "$(ProjectDir)\\src\\proto"',
'protoc --error_format=msvs -I=%(RelativeDir) --cpp_out=src\\proto %(Identity)'}
buildoutputs {'$(ProjectDir)\\src\\proto\\%(Filename).pb.cc', '$(ProjectDir)\\src\\proto\\%(Filename).pb.h'}

View File

@ -0,0 +1,231 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "auth.hpp"
#include "component/command.hpp"
#include "network.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/smbios.hpp>
#include <utils/info_string.hpp>
#include <utils/cryptography.hpp>
#include "game/game.hpp"
#include "steam/steam.hpp"
namespace auth
{
namespace
{
std::string get_hdd_serial()
{
DWORD serial{};
if (!GetVolumeInformationA("C:\\", nullptr, 0, &serial, nullptr, nullptr, nullptr, 0))
{
return {};
}
return utils::string::va("%08X", serial);
}
std::string get_hw_profile_guid()
{
HW_PROFILE_INFO info;
if (!GetCurrentHwProfileA(&info))
{
return {};
}
return std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
}
std::string get_protected_data()
{
std::string input = "X-Labs-S1x-Auth";
DATA_BLOB data_in{}, data_out{};
data_in.pbData = reinterpret_cast<uint8_t*>(input.data());
data_in.cbData = static_cast<DWORD>(input.size());
if (CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE)
{
return {};
}
const auto size = std::min(data_out.cbData, 52ul);
std::string result{ reinterpret_cast<char*>(data_out.pbData), size };
LocalFree(data_out.pbData);
return result;
}
std::string get_key_entropy()
{
std::string entropy{};
entropy.append(utils::smbios::get_uuid());
entropy.append(get_hw_profile_guid());
entropy.append(get_protected_data());
entropy.append(get_hdd_serial());
if (entropy.empty())
{
entropy.resize(32);
utils::cryptography::random::get_data(entropy.data(), entropy.size());
}
return entropy;
}
utils::cryptography::ecc::key& get_key()
{
static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
return key;
}
int send_connect_data_stub(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len)
{
std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data());
const auto _ = gsl::finally([]()
{
game::SV_Cmd_EndTokenizedString();
});
const command::params_sv params;
if (params.size() < 3)
{
return false;
}
const utils::info_string info_string{ std::string{params[2]} };
const auto challenge = info_string.get("challenge");
connect_string.clear();
connect_string.append(params[0]);
connect_string.append(" ");
connect_string.append(params[1]);
connect_string.append(" ");
connect_string.append("\"" + info_string.build() + "\"");
proto::network::connect_info info;
info.set_publickey(get_key().get_public_key());
info.set_signature(sign_message(get_key(), challenge));
info.set_infostring(connect_string);
network::send(*adr, "connect", info.SerializeAsString());
return true;
}
void direct_connect(game::netadr_s* from, game::msg_t* msg)
{
const auto offset = sizeof("connect") + 4;
proto::network::connect_info info;
if (!info.ParseFromArray(msg->data + offset, msg->cursize - offset))
{
network::send(*from, "error", "Invalid connect data!", '\n');
return;
}
game::SV_Cmd_EndTokenizedString();
game::SV_Cmd_TokenizeString(info.infostring().data());
const command::params_sv params;
if (params.size() < 3)
{
network::send(*from, "error", "Invalid connect string!", '\n');
return;
}
const utils::info_string info_string{ std::string{params[2]} };
const auto steam_id = info_string.get("xuid");
const auto challenge = info_string.get("challenge");
if (steam_id.empty() || challenge.empty())
{
network::send(*from, "error", "Invalid connect data!", '\n');
return;
}
utils::cryptography::ecc::key key;
key.set(info.publickey());
const auto xuid = strtoull(steam_id.data(), nullptr, 16);
if (xuid != key.get_hash())
{
//MessageBoxA(nullptr, steam_id.data(), std::to_string(key.get_hash()).data(), 0);
network::send(*from, "error",
utils::string::va("XUID doesn't match the certificate: %llX != %llX", xuid, key.get_hash()), '\n');
return;
}
if (!key.is_valid() || !verify_message(key, challenge, info.signature()))
{
network::send(*from, "error", "Challenge signature was invalid!", '\n');
return;
}
game::SV_DirectConnect(from);
}
void* get_direct_connect_stub()
{
return utils::hook::assemble([](utils::hook::assembler& a)
{
a.lea(rcx, qword_ptr(rsp, 0x20));
a.movaps(xmmword_ptr(rsp, 0x20), xmm0);
a.pushad64();
a.mov(rdx, rdi);
a.call_aligned(direct_connect);
a.popad64();
a.jmp(0x140488CE2); // H1MP64(1.4)
});
}
}
uint64_t get_guid()
{
if (game::environment::is_dedi())
{
return 0x110000100000000 | (::utils::cryptography::random::get_integer() & ~0x80000000);
}
return get_key().get_hash();
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// Patch steam id bit check
if (game::environment::is_sp())
{
utils::hook::jump(0x1404267F0, 0x140426846); // S1SP
utils::hook::jump(0x14042760F, 0x140427650); // S1SP
utils::hook::jump(0x140427AB4, 0x140427B02); // S1SP
}
else
{
utils::hook::jump(0x140571E07, 0x140571E5A); // H1MP64[1.4]
utils::hook::jump(0x14004B223, 0x14004B4F2); // H1MP64[1.4]
utils::hook::jump(0x14004B4AD, 0x140009B48); // H1MP64[1.4]
utils::hook::jump(0x140572F6F, 0x140572FB0); // H1MP64[1.4]
utils::hook::jump(0x140573470, 0x1405734B6); // H1MP64[1.4]
utils::hook::jump(0x140488BC1, get_direct_connect_stub(), true); // H1MP64[1.4]
utils::hook::call(0x140250ED2, send_connect_data_stub); // H1MP64[1.4]
}
command::add("guid", []()
{
printf("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
});
}
};
}
REGISTER_COMPONENT(auth::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace auth
{
uint64_t get_guid();
}

View File

@ -0,0 +1,60 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "localized_strings.hpp"
#include "scheduler.hpp"
#include "command.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace branding
{
game::dvar_t* dvar_draw2d;
class component final : public component_interface
{
public:
void post_unpack() override
{
//localized_strings::override("MENU_SP_CAMPAIGN", "COD DEVELOPER COMPANION");
localized_strings::override("LUA_MENU_MULTIPLAYER_CAPS", "H1-Mod: MULTIPLAYER\n");
localized_strings::override("MENU_MULTIPLAYER_CAPS", "H1-Mod: MULTIPLAYER");
localized_strings::override("PLATFORM_UI_HEADER_PLAY_MP_CAPS", "H1-ONLINE");
scheduler::loop([]()
{
if (!dvar_draw2d)
{
dvar_draw2d = game::Dvar_FindVar("cg_draw2d");
}
if (dvar_draw2d && !dvar_draw2d->current.enabled)
{
return;
}
const auto x = 4;
const auto y = 4;
const auto scale = 1.0f;
float color[4] = { 1.0f, 0.5f, 0.0f, 1.0f };
const auto* text = "H1-Mod 1.4";
auto* font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20);
if (!font) return;
game::R_AddCmdDrawText(text, 0x7FFFFFFF, font, static_cast<float>(x),
y + static_cast<float>(font->pixelHeight) * scale,
scale, scale, 0.0f, color, 0);
}, scheduler::pipeline::renderer);
}
};
}
REGISTER_COMPONENT(branding::component)
// fonts/default.otf, fonts/defaultBold.otf, fonts/fira_mono_regular.ttf, fonts/fira_mono_bold.ttf

View File

@ -0,0 +1,418 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/memory.hpp>
#include "utils/io.hpp"
namespace command
{
namespace
{
utils::hook::detour dvar_command_hook;
std::unordered_map<std::string, std::function<void(params&)>> handlers;
std::unordered_map<std::string, std::function<void(int, params_sv&)>> handlers_sv;
void main_handler()
{
params params = {};
const auto command = utils::string::to_lower(params[0]);
if (handlers.find(command) != handlers.end())
{
handlers[command](params);
}
}
void client_command(const int client_num, void* a2)
{
params_sv params = {};
const auto command = utils::string::to_lower(params[0]);
if (handlers_sv.find(command) != handlers_sv.end())
{
handlers_sv[command](client_num, params);
}
dvar_command_hook.invoke<void>(client_num, a2);
}
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride)
{
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](game::XAssetHeader header, void* data)
{
const auto& cb = *static_cast<const std::function<void(game::XAssetHeader)>*>(data);
cb(header);
}), &callback, includeOverride);
}
game::dvar_t* dvar_command_stub()
{
const params args;
if (args.size() <= 0)
{
return 0;
}
const auto dvar = game::Dvar_FindVar(args[0]);
if (dvar)
{
if (args.size() == 1)
{
const auto current = game::Dvar_ValueToString(dvar, dvar->current, 0);
const auto reset = game::Dvar_ValueToString(dvar, dvar->reset, 0);
game_console::print(game_console::con_type_info, "\"%s\" is: \"%s\" default: \"%s\" hash: %i",
args[0], current, reset, dvar->name);
game_console::print(game_console::con_type_info, " %s\n",
dvars::dvar_get_domain(dvar->type, dvar->domain).data());
}
//else
//{
// char command[0x1000] = { 0 };
// game::Dvar_GetCombinedString(command, 1);
// game::Dvar_SetCommand(dvar->name, command);
//}
return dvar;
}
return 0;
}
}
params::params()
: nesting_(game::cmd_args->nesting)
{
}
int params::size() const
{
return game::cmd_args->argc[this->nesting_];
}
const char* params::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return game::cmd_args->argv[this->nesting_][index];
}
std::string params::join(const int index) const
{
std::string result = {};
for (auto i = index; i < this->size(); i++)
{
if (i > index) result.append(" ");
result.append(this->get(i));
}
return result;
}
params_sv::params_sv()
: nesting_(game::sv_cmd_args->nesting)
{
}
int params_sv::size() const
{
return game::sv_cmd_args->argc[this->nesting_];
}
const char* params_sv::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return game::sv_cmd_args->argv[this->nesting_][index];
}
std::string params_sv::join(const int index) const
{
std::string result = {};
for (auto i = index; i < this->size(); i++)
{
if (i > index) result.append(" ");
result.append(this->get(i));
}
return result;
}
void add_raw(const char* name, void (*callback)())
{
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>());
}
void add(const char* name, const std::function<void(const params&)>& callback)
{
const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end())
add_raw(name, main_handler);
handlers[command] = callback;
}
void add(const char* name, const std::function<void()>& callback)
{
add(name, [callback](const params&)
{
callback();
});
}
void add_sv(const char* name, std::function<void(int, const params_sv&)> callback)
{
// doing this so the sv command would show up in the console
add_raw(name, nullptr);
const auto command = utils::string::to_lower(name);
if (handlers_sv.find(command) == handlers_sv.end())
handlers_sv[command] = std::move(callback);
}
void execute(std::string command, const bool sync)
{
command += "\n";
if (sync)
{
game::Cmd_ExecuteSingleCommand(0, 0, command.data());
}
else
{
game::Cbuf_AddText(0, command.data());
}
}
void parse_command_line()
{
static auto parsed = false;
if (parsed)
{
return;
}
static std::string comand_line_buffer = GetCommandLineA();
auto* command_line = comand_line_buffer.data();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x142623FB4);
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0);
auto inq = false;
com_console_lines[0] = command_line;
com_num_console_lines = 0;
while (*command_line)
{
if (*command_line == '"')
{
inq = !inq;
}
// look for a + separating character
// if commandLine came from a file, we might have real line seperators
if ((*command_line == '+' && !inq) || *command_line == '\n' || *command_line == '\r')
{
if (com_num_console_lines == 0x20) // MAX_CONSOLE_LINES
{
break;
}
com_console_lines[com_num_console_lines] = command_line + 1;
com_num_console_lines++;
*command_line = '\0';
}
command_line++;
}
parsed = true;
}
void parse_commandline_stub()
{
parse_command_line();
reinterpret_cast<void(*)()>(0x1400D8210)(); // mwr: test
}
void read_startup_variable(const std::string& dvar)
{
// parse the commandline if it's not parsed
parse_command_line();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x142623FB4);
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0);
for (int i = 0; i < com_num_console_lines; i++)
{
game::Cmd_TokenizeString(com_console_lines[i]);
// only +set dvar value
if (game::Cmd_Argc() >= 3 && game::Cmd_Argv(0) == "set"s && game::Cmd_Argv(1) == dvar)
{
game::Dvar_SetCommand(game::Cmd_Argv(1), game::Cmd_Argv(2));
}
game::Cmd_EndTokenizeString();
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
//utils::hook::jump(game::base_address + 0x000000, dvar_command_stub, true); // H1
static game::cmd_function_s cmd_test;
game::Cmd_AddCommandInternal("quit", game::Com_Quit_f, &cmd_test);
/*game::Cmd_AddCommandInternal("connect", []() {
}, game::cmd_function_s);*/
//add_raw("quit", main_handler);
//add("quit", game::Com_Quit_f);
//add("startmap", [](const params& params)
//{
// const auto map = params.get(1);
// const auto exists = utils::hook::invoke<bool>(game::base_address + 0x412B50, map, 0);
// if (!exists)
// {
// game_console::print(game_console::con_type_error, "map '%s' not found\n", map);
// return;
// }
// // SV_SpawnServer
// utils::hook::invoke<void>(game::base_address + 0x6B3AA0, map, 0, 0, 0, 0);
//});
/*add("say", [](const params& params)
{
chat::print(params.join(1));
});*/
//add("listassetpool", [](const params& params)
//{
// if (params.size() < 2)
// {
// game_console::print(game_console::con_type_info, "listassetpool <poolnumber>: list all the assets in the specified pool\n");
// for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
// {
// game_console::print(game_console::con_type_info, "%d %s\n", i, game::g_assetNames[i]);
// }
// }
// else
// {
// const auto type = static_cast<game::XAssetType>(atoi(params.get(1)));
// if (type < 0 || type >= game::XAssetType::ASSET_TYPE_COUNT)
// {
// game_console::print(game_console::con_type_info, "Invalid pool passed must be between [%d, %d]\n", 0, game::XAssetType::ASSET_TYPE_COUNT - 1);
// return;
// }
// game_console::print(game_console::con_type_info, "Listing assets in pool %s\n", game::g_assetNames[type]);
// enum_assets(type, [type](const game::XAssetHeader header)
// {
// const auto asset = game::XAsset{type, header};
// const auto* const asset_name = game::DB_GetXAssetName(&asset);
// //const auto entry = game::DB_FindXAssetEntry(type, asset_name);
// //TODO: display which zone the asset is from
// game_console::print(game_console::con_type_info, "%s\n", asset_name);
// }, true);
// }
//});
//add("commandDump", []()
//{
// printf("======== Start command dump =========\n");
// game::cmd_function_s* cmd = (*game::cmd_functions);
// while (cmd)
// {
// if (cmd->name)
// {
// game_console::print(game_console::con_type_info, "%s\n", cmd->name);
// }
// cmd = cmd->next;
// }
// printf("======== End command dump =========\n");
//});
/*add("god", []()
{
if (!game::SV_Loaded())
{
return;
}
game::mp::g_entities[0].flags ^= game::FL_GODMODE;
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::g_entities[0].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
});*/
//add("notarget", []()
//{
// if (!game::SV_Loaded())
// {
// return;
// }
// game::g_entities[0].flags ^= game::FL_NOTARGET;
// game::CG_GameMessage(0, utils::string::va("notarget %s",
// game::g_entities[0].flags & game::FL_NOTARGET
// ? "^2on"
// : "^1off"));
//});
//add("noclip", []()
//{
// if (!game::SV_Loaded())
// {
// return;
// }
// game::g_entities[0].client->flags ^= 1;
// game::CG_GameMessage(0, utils::string::va("noclip %s",
// game::g_entities[0].client->flags & 1
// ? "^2on"
// : "^1off"));
//});
}
};
}
REGISTER_COMPONENT(command::component)

View File

@ -0,0 +1,50 @@
#pragma once
namespace command
{
class params
{
public:
params();
int size() const;
const char* get(int index) const;
std::string join(int index) const;
const char* operator[](const int index) const
{
return this->get(index); //
}
private:
int nesting_;
};
class params_sv
{
public:
params_sv();
int size() const;
const char* get(int index) const;
std::string join(int index) const;
const char* operator[](const int index) const
{
return this->get(index); //
}
private:
int nesting_;
};
void read_startup_variable(const std::string& dvar);
void add_raw(const char* name, void (*callback)());
void add(const char* name, const std::function<void(const params&)>& callback);
void add(const char* name, const std::function<void()>& callback);
void add_sv(const char* name, std::function<void(int, const params_sv&)> callback);
void execute(std::string command, bool sync = false);
}

View File

@ -0,0 +1,265 @@
#include <std_include.hpp>
#include "console.hpp"
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "command.hpp"
#include <utils/thread.hpp>
#include <utils/flags.hpp>
#include <utils/concurrency.hpp>
#include <utils/hook.hpp>
namespace game_console
{
void print(int type, const std::string& data);
}
namespace console
{
namespace
{
using message_queue = std::queue<std::string>;
utils::concurrency::container<message_queue> messages;
void hide_console()
{
auto* const con_window = GetConsoleWindow();
DWORD process;
GetWindowThreadProcessId(con_window, &process);
if (process == GetCurrentProcessId() || IsDebuggerPresent())
{
ShowWindow(con_window, SW_HIDE);
}
}
std::string format(va_list* ap, const char* message)
{
static thread_local char buffer[0x1000];
const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
if (count < 0) return {};
return { buffer, static_cast<size_t>(count) };
}
void dispatch_message(const int type, const std::string& message)
{
/*if (rcon::message_redirect(message))
{
return;
}*/
}
void append_text(const char* text)
{
dispatch_message(con_type_info, text);
}
}
class component final : public component_interface
{
public:
component()
{
hide_console();
(void)_pipe(this->handles_, 1024, _O_TEXT);
(void)_dup2(this->handles_[1], 1);
(void)_dup2(this->handles_[1], 2);
//setvbuf(stdout, nullptr, _IONBF, 0);
//setvbuf(stderr, nullptr, _IONBF, 0);
}
void post_start() override
{
this->terminate_runner_ = false;
this->console_runner_ = utils::thread::create_named_thread("Console IO", [this]
{
this->runner();
});
}
void pre_destroy() override
{
this->terminate_runner_ = true;
printf("\r\n");
_flushall();
if (this->console_runner_.joinable())
{
this->console_runner_.join();
}
if (this->console_thread_.joinable())
{
this->console_thread_.join();
}
_close(this->handles_[0]);
_close(this->handles_[1]);
messages.access([&](message_queue& msgs)
{
msgs = {};
});
}
void post_unpack() override
{
// Redirect input (]command)
utils::hook::jump(SELECT_VALUE(0x000000000, 0x1405141E0), append_text); // H1MP1.4
this->initialize();
}
private:
volatile bool console_initialized_ = false;
volatile bool terminate_runner_ = false;
std::thread console_runner_;
std::thread console_thread_;
int handles_[2]{};
void initialize()
{
this->console_thread_ = utils::thread::create_named_thread("Console", [this]()
{
if (game::environment::is_dedi() || !utils::flags::has_flag("noconsole"))
{
game::Sys_ShowConsole();
}
if (!game::environment::is_dedi())
{
// Hide that shit
ShowWindow(console::get_window(), SW_MINIMIZE);
}
{
messages.access([&](message_queue&)
{
this->console_initialized_ = true;
});
}
MSG msg;
while (!this->terminate_runner_)
{
if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
command::execute("quit", false);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
this->log_messages();
std::this_thread::sleep_for(1ms);
}
}
});
}
void log_messages()
{
/*while*/
if (this->console_initialized_ && !messages.get_raw().empty())
{
std::queue<std::string> message_queue_copy;
{
messages.access([&](message_queue& msgs)
{
message_queue_copy = std::move(msgs);
msgs = {};
});
}
while (!message_queue_copy.empty())
{
log_message(message_queue_copy.front());
message_queue_copy.pop();
}
}
fflush(stdout);
fflush(stderr);
}
static void log_message(const std::string& message)
{
OutputDebugStringA(message.data());
game::Conbuf_AppendText(message.data()); //0x140513FF0
FILE* pFile = fopen("debug.log", "a");
fprintf(pFile, "%s\n", message.data());
fclose(pFile);
}
void runner()
{
char buffer[1024];
while (!this->terminate_runner_ && this->handles_[0])
{
const auto len = _read(this->handles_[0], buffer, sizeof(buffer));
if (len > 0)
{
dispatch_message(con_type_info, std::string(buffer, len));
}
else
{
std::this_thread::sleep_for(1ms);
}
}
std::this_thread::yield();
}
};
HWND get_window()
{
return *reinterpret_cast<HWND*>((SELECT_VALUE(0x000000000, 0x14DDFC2D0))); // H1MP1.4
}
void set_title(std::string title)
{
SetWindowText(get_window(), title.data());
}
void set_size(const int width, const int height)
{
RECT rect;
GetWindowRect(get_window(), &rect);
SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0);
auto* const logo_window = *reinterpret_cast<HWND*>(SELECT_VALUE(0x000000000, 0x14DDFC2E0)); // H1MP64(1.4)
SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0);
}
void print(const int type, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
const auto result = format(&ap, fmt);
va_end(ap);
dispatch_message(type, result);
}
}
REGISTER_COMPONENT(console::component)

View File

@ -0,0 +1,35 @@
#pragma once
namespace console
{
HWND get_window();
void set_title(std::string title);
void set_size(int width, int height);
enum console_type
{
con_type_error = 1,
con_type_warning = 3,
con_type_info = 7
};
void print(int type, const char* fmt, ...);
template <typename... Args>
void error(const char* fmt, Args&&... args)
{
print(con_type_error, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(const char* fmt, Args&&... args)
{
print(con_type_warning, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void info(const char* fmt, Args&&... args)
{
print(con_type_info, fmt, std::forward<Args>(args)...);
}
}

View File

@ -0,0 +1,453 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/thread.hpp>
#include "game/game.hpp"
#include "game/demonware/servers/lobby_server.hpp"
#include "game/demonware/servers/auth3_server.hpp"
#include "game/demonware/servers/stun_server.hpp"
#include "game/demonware/servers/umbrella_server.hpp"
#include "game/demonware/server_registry.hpp"
#include <game/dvars.hpp>
#define TCP_BLOCKING true
#define UDP_BLOCKING false
namespace demonware
{
namespace
{
volatile bool exit_server;
std::thread server_thread;
utils::concurrency::container<std::unordered_map<SOCKET, bool>> blocking_sockets;
utils::concurrency::container<std::unordered_map<SOCKET, tcp_server*>> socket_map;
server_registry<tcp_server> tcp_servers;
server_registry<udp_server> udp_servers;
tcp_server* find_server(const SOCKET socket)
{
return socket_map.access<tcp_server*>([&](const std::unordered_map<SOCKET, tcp_server*>& map) -> tcp_server*
{
const auto entry = map.find(socket);
if (entry == map.end())
{
return nullptr;
}
return entry->second;
});
}
bool socket_link(const SOCKET socket, const uint32_t address)
{
auto* server = tcp_servers.find(address);
if (!server)
{
return false;
}
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& map)
{
map[socket] = server;
});
return true;
}
void socket_unlink(const SOCKET socket)
{
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& map)
{
const auto entry = map.find(socket);
if (entry != map.end())
{
map.erase(entry);
}
});
}
bool is_socket_blocking(const SOCKET socket, const bool def)
{
return blocking_sockets.access<bool>([&](std::unordered_map<SOCKET, bool>& map)
{
const auto entry = map.find(socket);
if (entry == map.end())
{
return def;
}
return entry->second;
});
}
void remove_blocking_socket(const SOCKET socket)
{
blocking_sockets.access([&](std::unordered_map<SOCKET, bool>& map)
{
const auto entry = map.find(socket);
if (entry != map.end())
{
map.erase(entry);
}
});
}
void add_blocking_socket(const SOCKET socket, const bool block)
{
blocking_sockets.access([&](std::unordered_map<SOCKET, bool>& map)
{
map[socket] = block;
});
}
void server_main()
{
exit_server = false;
while (!exit_server)
{
tcp_servers.frame();
udp_servers.frame();
std::this_thread::sleep_for(50ms);
}
}
namespace io
{
hostent* gethostbyname_stub(const char* name)
{
#ifdef DEBUG
printf("[ network ]: [gethostbyname]: \"%s\"\n", name);
#endif
base_server* server = tcp_servers.find(name);
if (!server)
{
server = udp_servers.find(name);
}
if (!server)
{
#pragma warning(push)
#pragma warning(disable: 4996)
return gethostbyname(name);
#pragma warning(pop)
}
static thread_local in_addr address{};
address.s_addr = server->get_address();
static thread_local in_addr* addr_list[2]{};
addr_list[0] = &address;
addr_list[1] = nullptr;
static thread_local hostent host{};
host.h_name = const_cast<char*>(name);
host.h_aliases = nullptr;
host.h_addrtype = AF_INET;
host.h_length = sizeof(in_addr);
host.h_addr_list = reinterpret_cast<char**>(addr_list);
return &host;
}
int connect_stub(const SOCKET s, const struct sockaddr* addr, const int len)
{
if (len == sizeof(sockaddr_in))
{
const auto* in_addr = reinterpret_cast<const sockaddr_in*>(addr);
if (socket_link(s, in_addr->sin_addr.s_addr)) return 0;
}
return connect(s, addr, len);
}
int closesocket_stub(const SOCKET s)
{
remove_blocking_socket(s);
socket_unlink(s);
return closesocket(s);
}
int send_stub(const SOCKET s, const char* buf, const int len, const int flags)
{
auto* server = find_server(s);
if (server)
{
server->handle_input(buf, len);
return len;
}
return send(s, buf, len, flags);
}
int recv_stub(const SOCKET s, char* buf, const int len, const int flags)
{
auto* server = find_server(s);
if (server)
{
if (server->pending_data())
{
return static_cast<int>(server->handle_output(buf, len));
}
else
{
WSASetLastError(WSAEWOULDBLOCK);
return -1;
}
}
return recv(s, buf, len, flags);
}
int sendto_stub(const SOCKET s, const char* buf, const int len, const int flags, const sockaddr* to,
const int tolen)
{
const auto* in_addr = reinterpret_cast<const sockaddr_in*>(to);
auto* server = udp_servers.find(in_addr->sin_addr.s_addr);
if (server)
{
server->handle_input(buf, len, { s, to, tolen });
return len;
}
return sendto(s, buf, len, flags, to, tolen);
}
int recvfrom_stub(const SOCKET s, char* buf, const int len, const int flags, struct sockaddr* from,
int* fromlen)
{
// Not supported yet
if (is_socket_blocking(s, UDP_BLOCKING))
{
return recvfrom(s, buf, len, flags, from, fromlen);
}
size_t result = 0;
udp_servers.for_each([&](udp_server& server)
{
if (server.pending_data(s))
{
result = server.handle_output(
s, buf, static_cast<size_t>(len), from, fromlen);
}
});
if (result)
{
return static_cast<int>(result);
}
return recvfrom(s, buf, len, flags, from, fromlen);
}
int select_stub(const int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
struct timeval* timeout)
{
if (exit_server)
{
return select(nfds, readfds, writefds, exceptfds, timeout);
}
auto result = 0;
std::vector<SOCKET> read_sockets;
std::vector<SOCKET> write_sockets;
socket_map.access([&](std::unordered_map<SOCKET, tcp_server*>& sockets)
{
for (auto& s : sockets)
{
if (readfds)
{
if (FD_ISSET(s.first, readfds))
{
if (s.second->pending_data())
{
read_sockets.push_back(s.first);
FD_CLR(s.first, readfds);
}
}
}
if (writefds)
{
if (FD_ISSET(s.first, writefds))
{
write_sockets.push_back(s.first);
FD_CLR(s.first, writefds);
}
}
if (exceptfds)
{
if (FD_ISSET(s.first, exceptfds))
{
FD_CLR(s.first, exceptfds);
}
}
}
});
if ((!readfds || readfds->fd_count == 0) && (!writefds || writefds->fd_count == 0))
{
timeout->tv_sec = 0;
timeout->tv_usec = 0;
}
result = select(nfds, readfds, writefds, exceptfds, timeout);
if (result < 0) result = 0;
for (const auto& socket : read_sockets)
{
if (readfds)
{
FD_SET(socket, readfds);
result++;
}
}
for (const auto& socket : write_sockets)
{
if (writefds)
{
FD_SET(socket, writefds);
result++;
}
}
return result;
}
int ioctlsocket_stub(const SOCKET s, const long cmd, u_long* argp)
{
if (static_cast<unsigned long>(cmd) == (FIONBIO))
{
add_blocking_socket(s, *argp == 0);
}
return ioctlsocket(s, cmd, argp);
}
BOOL internet_get_connected_state_stub(LPDWORD, DWORD)
{
// Allow offline play
return TRUE;
}
}
void bd_logger_stub(const char* const function, const char* const msg, ...)
{
game::dvar_t* enabled;
enabled = dvars::register_bool("bd_logger_enabled", false, game::DVAR_FLAG_SAVED, true);
//game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_SAVED, "bdLogger")
if (!enabled->current.enabled)
{
return;
}
char buffer[2048];
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
printf("%s: %s\n", function, buffer);
va_end(ap);
}
}
class component final : public component_interface
{
public:
component()
{
udp_servers.create<stun_server>("phoenix.stun.us.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.eu.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.jp.demonware.net");
udp_servers.create<stun_server>("phoenix.stun.au.demonware.net");
udp_servers.create<stun_server>("stun.us.demonware.net");
udp_servers.create<stun_server>("stun.eu.demonware.net");
udp_servers.create<stun_server>("stun.jp.demonware.net");
udp_servers.create<stun_server>("stun.au.demonware.net");
tcp_servers.create<auth3_server>("mwr-pc-steam-auth3.prod.demonware.net");
tcp_servers.create<lobby_server>("mwr-pc-steam-lobby.prod.demonware.net");
tcp_servers.create<umbrella_server>("prod.umbrella.demonware.net");
}
void post_load() override
{
server_thread = utils::thread::create_named_thread("Demonware", server_main);
}
void* load_import(const std::string& library, const std::string& function) override
{
if (library == "WS2_32.dll")
{
if (function == "#3") return io::closesocket_stub;
if (function == "#4") return io::connect_stub;
if (function == "#10") return io::ioctlsocket_stub;
if (function == "#16") return io::recv_stub;
if (function == "#17") return io::recvfrom_stub;
if (function == "#18") return io::select_stub;
if (function == "#19") return io::send_stub;
if (function == "#20") return io::sendto_stub;
if (function == "#52") return io::gethostbyname_stub;
}
if (function == "InternetGetConnectedState")
{
return io::internet_get_connected_state_stub;
}
return nullptr;
}
void post_unpack() override
{
utils::hook::jump(SELECT_VALUE(0, 0x1407400B0), bd_logger_stub); // H1MP64(1.4)
//singleplayer not supported so far.
if (game::environment::is_sp())
{
utils::hook::set<uint8_t>(0x1405632E0, 0xC3); // bdAuthSteam
utils::hook::set<uint8_t>(0x1402DF2C0, 0xC3); // dwNet
return;
}
utils::hook::set<uint8_t>(0x140715039, 0x0); // CURLOPT_SSL_VERIFYPEER H1MP64(1.4)
utils::hook::set<uint8_t>(0x140715025, 0xAF); // CURLOPT_SSL_VERIFYHOST H1MP64(1.4)
utils::hook::set<uint8_t>(0x14095433C, 0x0); // HTTPS -> HTTP [MWR OK][S1X: 0x14088D0E8]
//HTTPS -> HTTP
utils::hook::inject(0x14006DDA9, "http://prod.umbrella.demonware.net/v1.0/"); // ---> [H1MP1.4 - S1X: 0x14003852E]
utils::hook::inject(0x14006E11C, "http://prod.umbrella.demonware.net/v1.0/"); // ---> [H1MP1.4 - S1X: 0x14003884F]
utils::hook::inject(0x14006E2FB, "http://prod.umbrella.demonware.net/v1.0/"); // ---> [H1MP1.4 - S1X: 0x140038A07]
utils::hook::set<uint8_t>(0x14047F290, 0xC3); // SV_SendMatchData H1MP64(1.4)
utils::hook::set<uint8_t>(0x140598990, 0xC3); // Live_CheckForFullDisconnect H1MP64(1.4)
}
void pre_destroy() override
{
exit_server = true;
if (server_thread.joinable())
{
server_thread.join();
}
}
};
}
REGISTER_COMPONENT(demonware::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace demonware
{
}

View File

@ -0,0 +1,756 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game_console.hpp"
#include "command.hpp"
#include "scheduler.hpp"
#include <utils/string.hpp>
#include <utils/hook.hpp>
#define console_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 18)
#define material_white game::Material_RegisterHandle("white")
namespace game_console
{
namespace
{
struct console_globals
{
float x;
float y;
float left_x;
float font_height;
bool may_auto_complete;
char auto_complete_choice[64];
int info_line_count;
};
struct ingame_console
{
char buffer[256];
int cursor;
int font_height;
int visible_line_count;
int visible_pixel_width;
float screen_min[2]; //left & top
float screen_max[2]; //right & bottom
console_globals globals;
bool output_visible;
int display_line_offset;
int line_count;
std::deque<std::string> output;
};
ingame_console con;
std::int32_t history_index = -1;
std::deque<std::string> history;
std::string fixed_input;
std::vector<std::string> matches;
float color_white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
float color_title[4] = { 0.9f, 0.9f, 0.5f, 1.0f };
void clear()
{
strncpy_s(con.buffer, "", 256);
con.cursor = 0;
fixed_input = "";
matches.clear();
}
void print(const std::string& data)
{
if (con.visible_line_count > 0 && con.display_line_offset == (con.output.size() - con.visible_line_count))
{
con.display_line_offset++;
}
con.output.push_back(data);
printf("%s\n", data.data());
if (con.output.size() > 1024)
{
con.output.pop_front();
}
}
void toggle_console()
{
clear();
con.output_visible = false;
*game::keyCatchers ^= 1;
}
void toggle_console_output()
{
con.output_visible = con.output_visible == 0;
}
void check_resize()
{
con.screen_min[0] = 6.0f;
con.screen_min[1] = 6.0f;
con.screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 6.0f;
con.screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1] - 6.0f;
if (console_font)
{
con.font_height = console_font->pixelHeight;
con.visible_line_count = static_cast<int>((con.screen_max[1] - con.screen_min[1] - (con.font_height * 2)
) -
24.0f) / con.font_height;
con.visible_pixel_width = static_cast<int>(((con.screen_max[0] - con.screen_min[0]) - 10.0f) - 18.0f);
}
else
{
con.font_height = 0;
con.visible_line_count = 0;
con.visible_pixel_width = 0;
}
}
void draw_box(const float x, const float y, const float w, const float h, float* color)
{
game::vec4_t dark_color;
dark_color[0] = color[0] * 0.5f;
dark_color[1] = color[1] * 0.5f;
dark_color[2] = color[2] * 0.5f;
dark_color[3] = color[3];
game::R_AddCmdDrawStretchPic(x, y, w, h, 0.0f, 0.0f, 0.0f, 0.0f, color, material_white);
game::R_AddCmdDrawStretchPic(x, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white);
game::R_AddCmdDrawStretchPic((x + w) - 2.0f, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
material_white);
game::R_AddCmdDrawStretchPic(x, y, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white);
game::R_AddCmdDrawStretchPic(x, (y + h) - 2.0f, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
material_white);
}
void draw_input_box(const int lines, float* color)
{
draw_box(
con.globals.x - 6.0f,
con.globals.y - 6.0f,
(con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]),
(lines * con.globals.font_height) + 12.0f,
color);
}
void draw_input_text_and_over(const char* str, float* color)
{
game::R_AddCmdDrawText(str, 0x7FFFFFFF, console_font, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f,
1.0f, 0.0f, color, 0);
con.globals.x = game::R_TextWidth(str, 0, console_font) + con.globals.x + 6.0f;
}
void draw_hint_box(const int lines, float* color, [[maybe_unused]] float offset_x = 0.0f,
[[maybe_unused]] float offset_y = 0.0f)
{
const auto _h = lines * con.globals.font_height + 12.0f;
const auto _y = con.globals.y - 3.0f + con.globals.font_height + 12.0f;
const auto _w = (con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]);
draw_box(con.globals.x - 6.0f, _y, _w, _h, color);
}
void draw_hint_text(const int line, const char* text, float* color, const float offset = 0.0f)
{
const auto _y = con.globals.font_height + con.globals.y + (con.globals.font_height * (line + 1)) + 15.0f;
game::R_AddCmdDrawText(text, 0x7FFFFFFF, console_font, con.globals.x + offset, _y, 1.0f, 1.0f, 0.0f, color, 0);
}
bool match_compare(const std::string& input, const std::string& text, const bool exact)
{
if (exact && text == input) return true;
if (!exact && text.find(input) != std::string::npos) return true;
return false;
}
void find_matches(std::string input, std::vector<std::string>& suggestions, const bool exact)
{
input = utils::string::to_lower(input);
for (const auto& dvar : dvars::dvar_list)
{
auto name = utils::string::to_lower(dvar);
if (match_compare(input, name, exact))
{
suggestions.push_back(dvar);
}
if (exact && suggestions.size() > 1)
{
return;
}
}
if (suggestions.size() == 0 && game::Dvar_FindVar(input.data()))
{
suggestions.push_back(input.data());
}
game::cmd_function_s* cmd = (*game::cmd_functions);
while (cmd)
{
if (cmd->name)
{
std::string name = utils::string::to_lower(cmd->name);
if (match_compare(input, name, exact))
{
suggestions.push_back(cmd->name);
}
if (exact && suggestions.size() > 1)
{
return;
}
}
cmd = cmd->next;
}
}
void draw_input()
{
con.globals.font_height = static_cast<float>(console_font->pixelHeight);
con.globals.x = con.screen_min[0] + 6.0f;
con.globals.y = con.screen_min[1] + 6.0f;
con.globals.left_x = con.screen_min[0] + 6.0f;
draw_input_box(1, dvars::con_inputBoxColor->current.vector);
draw_input_text_and_over("H1-Mod >", color_title);
con.globals.left_x = con.globals.x;
con.globals.auto_complete_choice[0] = 0;
game::R_AddCmdDrawTextWithCursor(con.buffer, 0x7FFFFFFF, console_font, 18, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0, color_white, 0,
con.cursor, '|');
//game::R_AddCmdDrawText(con.buffer, 0x7FFF, console_font, con.globals.x,
// con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0.0f, color_white, 0);
// check if using a prefixed '/' or not
const auto input = con.buffer[1] && (con.buffer[0] == '/' || con.buffer[0] == '\\')
? std::string(con.buffer).substr(1)
: std::string(con.buffer);
if (!input.length())
{
return;
}
if (input != fixed_input)
{
matches.clear();
if (input.find(" ") != std::string::npos)
{
find_matches(input.substr(0, input.find(" ")), matches, true);
}
else
{
find_matches(input, matches, false);
}
fixed_input = input;
}
con.globals.may_auto_complete = false;
if (matches.size() > 24)
{
draw_hint_box(1, dvars::con_inputHintBoxColor->current.vector);
draw_hint_text(0, utils::string::va("%i matches (too many to show here)", matches.size()),
dvars::con_inputDvarMatchColor->current.vector);
}
else if (matches.size() == 1)
{
auto* const dvar = game::Dvar_FindVar(matches[0].data());
const auto line_count = dvar ? 2 : 1;
draw_hint_box(line_count, dvars::con_inputHintBoxColor->current.vector);
draw_hint_text(0, matches[0].data(), dvar
? dvars::con_inputDvarMatchColor->current.vector
: dvars::con_inputCmdMatchColor->current.vector);
if (dvar)
{
const auto offset = (con.screen_max[0] - con.globals.x) / 2.5f;
draw_hint_text(0, game::Dvar_ValueToString(dvar, dvar->current, 0),
dvars::con_inputDvarValueColor->current.vector, offset);
draw_hint_text(1, " default", dvars::con_inputDvarInactiveValueColor->current.vector);
draw_hint_text(1, game::Dvar_ValueToString(dvar, dvar->reset, 0),
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
else if (matches.size() > 1)
{
draw_hint_box(static_cast<int>(matches.size()), dvars::con_inputHintBoxColor->current.vector);
const auto offset = (con.screen_max[0] - con.globals.x) / 2.5f;
for (size_t i = 0; i < matches.size(); i++)
{
auto* const dvar = game::Dvar_FindVar(matches[i].data());
draw_hint_text(static_cast<int>(i), matches[i].data(),
dvar
? dvars::con_inputDvarMatchColor->current.vector
: dvars::con_inputCmdMatchColor->current.vector);
if (dvar)
{
draw_hint_text(static_cast<int>(i), game::Dvar_ValueToString(dvar, dvar->current, 0),
dvars::con_inputDvarValueColor->current.vector, offset);
}
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
}
void draw_output_scrollbar(const float x, float y, const float width, const float height)
{
const auto _x = (x + width) - 10.0f;
draw_box(_x, y, 10.0f, height, dvars::con_outputBarColor->current.vector);
auto _height = height;
if (con.output.size() > con.visible_line_count)
{
const auto percentage = static_cast<float>(con.visible_line_count) / con.output.size();
_height *= percentage;
const auto remainingSpace = height - _height;
const auto percentageAbove = static_cast<float>(con.display_line_offset) / (con.output.size() - con.
visible_line_count);
y = y + (remainingSpace * percentageAbove);
}
draw_box(_x, y, 10.0f, _height, dvars::con_outputSliderColor->current.vector);
}
void draw_output_text(const float x, float y)
{
const auto offset = con.output.size() >= con.visible_line_count
? 0.0f
: (con.font_height * (con.visible_line_count - con.output.size()));
for (auto i = 0; i < con.visible_line_count; i++)
{
y = console_font->pixelHeight + y;
const auto index = i + con.display_line_offset;
if (index >= con.output.size())
{
break;
}
game::R_AddCmdDrawText(con.output.at(index).data(), 0x7FFF, console_font, x, y + offset, 1.0f, 1.0f,
0.0f, color_white, 0);
}
}
void draw_output_window()
{
draw_box(con.screen_min[0], con.screen_min[1] + 32.0f, con.screen_max[0] - con.screen_min[0],
(con.screen_max[1] - con.screen_min[1]) - 32.0f, dvars::con_outputWindowColor->current.vector);
const auto x = con.screen_min[0] + 6.0f;
const auto y = (con.screen_min[1] + 32.0f) + 6.0f;
const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f;
const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f;
game::R_AddCmdDrawText("H1-Mod 1.4", 0x7FFFFFFF, console_font, x,
((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_title, 0);
draw_output_scrollbar(x, y, width, height);
draw_output_text(x, y);
}
void draw_console()
{
check_resize();
if (*game::keyCatchers & 1)
{
if (!(*game::keyCatchers & 1))
{
con.output_visible = false;
}
if (con.output_visible)
{
draw_output_window();
}
draw_input();
}
}
}
void print(const int type, const char* fmt, ...)
{
char va_buffer[0x200] = { 0 };
va_list ap;
va_start(ap, fmt);
vsprintf_s(va_buffer, fmt, ap);
va_end(ap);
const auto formatted = std::string(va_buffer);
const auto lines = utils::string::split(formatted, '\n');
for (auto& line : lines)
{
print(type == con_type_info ? line : "^"s.append(std::to_string(type)).append(line));
}
}
bool console_char_event(const int localClientNum, const int key)
{
if (key == '`' || key == '~' || key == '|' || key == '\\')
{
return false;
}
if (key > 127)
{
return true;
}
if (*game::keyCatchers & 1)
{
if (key == game::keyNum_t::K_TAB) // tab (auto complete)
{
if (con.globals.may_auto_complete)
{
const auto firstChar = con.buffer[0];
clear();
if (firstChar == '\\' || firstChar == '/')
{
con.buffer[0] = firstChar;
con.buffer[1] = '\0';
}
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254)
{
con.buffer[con.cursor++] = ' ';
con.buffer[con.cursor] = '\0';
}
}
}
if (key == 'v' - 'a' + 1) // paste
{
const auto clipboard = utils::string::get_clipboard_data();
if (clipboard.empty())
{
return false;
}
for (auto i = 0; i < clipboard.length(); i++)
{
console_char_event(localClientNum, clipboard[i]);
}
return false;
}
if (key == 'c' - 'a' + 1) // clear
{
clear();
con.line_count = 0;
con.output.clear();
history_index = -1;
history.clear();
return false;
}
if (key == 'h' - 'a' + 1) // backspace
{
if (con.cursor > 0)
{
memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor,
strlen(con.buffer) + 1 - con.cursor);
con.cursor--;
}
return false;
}
if (key < 32)
{
return false;
}
if (con.cursor == 256 - 1)
{
return false;
}
memmove(con.buffer + con.cursor + 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor);
con.buffer[con.cursor] = static_cast<char>(key);
con.cursor++;
if (con.cursor == strlen(con.buffer) + 1)
{
con.buffer[con.cursor] = 0;
}
}
return true;
}
void execute(const char* cmd)
{
game::Cbuf_AddText(0, utils::string::va("%s \n", cmd));
}
bool console_key_event(const int localClientNum, const int key, const int down)
{
if (key == game::keyNum_t::K_GRAVE || key == game::keyNum_t::K_TILDE)
{
if (!down)
{
return false;
}
if (game::playerKeys[localClientNum].keys[game::keyNum_t::K_SHIFT].down)
{
if (!(*game::keyCatchers & 1))
toggle_console();
toggle_console_output();
return false;
}
toggle_console();
return false;
}
if (*game::keyCatchers & 1)
{
if (down)
{
if (key == game::keyNum_t::K_UPARROW)
{
if (++history_index >= history.size())
{
history_index = static_cast<int>(history.size()) - 1;
}
clear();
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
else if (key == game::keyNum_t::K_DOWNARROW)
{
if (--history_index < -1)
{
history_index = -1;
}
clear();
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
if (key == game::keyNum_t::K_RIGHTARROW)
{
if (con.cursor < strlen(con.buffer))
{
con.cursor++;
}
return false;
}
if (key == game::keyNum_t::K_LEFTARROW)
{
if (con.cursor > 0)
{
con.cursor--;
}
return false;
}
//scroll through output
if (key == game::keyNum_t::K_MWHEELUP || key == game::keyNum_t::K_PGUP)
{
if (con.output.size() > con.visible_line_count && con.display_line_offset > 0)
{
con.display_line_offset--;
}
}
else if (key == game::keyNum_t::K_MWHEELDOWN || key == game::keyNum_t::K_PGDN)
{
if (con.output.size() > con.visible_line_count && con.display_line_offset < (con.output.size() -
con.
visible_line_count))
{
con.display_line_offset++;
}
}
if (key == game::keyNum_t::K_ENTER)
{
execute(fixed_input.data());
if (history_index != -1)
{
const auto itr = history.begin() + history_index;
if (*itr == con.buffer)
{
history.erase(history.begin() + history_index);
}
}
history.push_front(con.buffer);
print(""s.append(con.buffer));
if (history.size() > 10)
{
history.erase(history.begin() + 10);
}
history_index = -1;
clear();
}
}
}
return true;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
scheduler::loop(draw_console, scheduler::pipeline::renderer);
con.cursor = 0;
con.visible_line_count = 0;
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
strncpy_s(con.buffer, "", 256);
con.globals.x = 0.0f;
con.globals.y = 0.0f;
con.globals.left_x = 0.0f;
con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false;
con.globals.info_line_count = 0;
strncpy_s(con.globals.auto_complete_choice, "", 64);
// //add clear command
//command::add("clear", [&]()
//{
//clear();
//con.line_count = 0;
//con.output.clear();
//history_index = -1;
//history.clear();
//});
char a2[1] = {};
// add our dvars
dvars::con_inputBoxColor = dvars::register_vec4(
"con_inputBoxColor",
0.2f, 0.2f, 0.2f, 0.9f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_inputHintBoxColor = dvars::register_vec4(
"con_inputHintBoxColor",
0.3f, 0.3f, 0.3f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_outputBarColor = dvars::register_vec4(
"con_outputBarColor",
0.5f, 0.5f, 0.5f, 0.6f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_outputSliderColor = dvars::register_vec4(
"con_outputSliderColor",
0.9f, 0.9f, 0.5f, 1.00f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_outputWindowColor = dvars::register_vec4(
"con_outputWindowColor",
0.25f, 0.25f, 0.25f, 0.85f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_inputDvarMatchColor = dvars::register_vec4(
"con_inputDvarMatchColor",
1.0f, 1.0f, 0.8f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_inputDvarValueColor = dvars::register_vec4(
"con_inputDvarValueColor",
1.0f, 1.0f, 0.8f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_inputDvarInactiveValueColor = dvars::register_vec4(
"con_inputDvarInactiveValueColor",
0.8f, 0.8f, 0.8f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
dvars::con_inputCmdMatchColor = dvars::register_vec4(
"con_inputCmdMatchColor",
0.80f, 0.80f, 1.0f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED);
}
};
}
REGISTER_COMPONENT(game_console::component)

View File

@ -0,0 +1,18 @@
#pragma once
namespace game_console
{
enum console_type
{
con_type_error = 1,
con_type_warning = 3,
con_type_info = 7
};
void print(int type, const char* fmt, ...);
bool console_char_event(int local_client_num, int key);
bool console_key_event(int local_client_num, int key, int down);
void execute(const char* cmd);
}

View File

@ -0,0 +1,118 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game_module.hpp"
#include <utils/hook.hpp>
namespace game_module
{
namespace
{
utils::hook::detour handle_a_hook;
utils::hook::detour handle_w_hook;
utils::hook::detour handle_ex_a_hook;
utils::hook::detour handle_ex_w_hook;
utils::hook::detour file_name_a_hook;
utils::hook::detour file_name_w_hook;
HMODULE __stdcall get_module_handle_a(const LPCSTR module_name)
{
if (!module_name)
{
return get_game_module();
}
return handle_a_hook.invoke<HMODULE>(module_name);
}
HMODULE __stdcall get_module_handle_w(const LPWSTR module_name)
{
if (!module_name)
{
return get_game_module();
}
return handle_w_hook.invoke<HMODULE>(module_name);
}
BOOL __stdcall get_module_handle_ex_a(const DWORD flags, const LPCSTR module_name, HMODULE* hmodule)
{
if (!module_name)
{
*hmodule = get_game_module();
return TRUE;
}
return handle_ex_a_hook.invoke<BOOL>(flags, module_name, hmodule);
}
BOOL __stdcall get_module_handle_ex_w(const DWORD flags, const LPCWSTR module_name, HMODULE* hmodule)
{
if (!module_name)
{
*hmodule = get_game_module();
return TRUE;
}
return handle_ex_w_hook.invoke<BOOL>(flags, module_name, hmodule);
}
DWORD __stdcall get_module_file_name_a(HMODULE hmodule, const LPSTR filename, const DWORD size)
{
if (!hmodule)
{
hmodule = get_game_module();
}
return file_name_a_hook.invoke<DWORD>(hmodule, filename, size);
}
DWORD __stdcall get_module_file_name_w(HMODULE hmodule, const LPWSTR filename, const DWORD size)
{
if (!hmodule)
{
hmodule = get_game_module();
}
return file_name_w_hook.invoke<DWORD>(hmodule, filename, size);
}
void hook_module_resolving()
{
handle_a_hook.create(&GetModuleHandleA, &get_module_handle_a);
handle_w_hook.create(&GetModuleHandleW, &get_module_handle_w);
handle_ex_w_hook.create(&GetModuleHandleExA, &get_module_handle_ex_a);
handle_ex_w_hook.create(&GetModuleHandleExW, &get_module_handle_ex_w);
file_name_a_hook.create(&GetModuleFileNameA, &get_module_file_name_a);
file_name_w_hook.create(&GetModuleFileNameW, &get_module_file_name_w);
}
}
utils::nt::library get_game_module()
{
static utils::nt::library game{ HMODULE(0x140000000) };
return game;
}
utils::nt::library get_host_module()
{
static utils::nt::library host{};
return host;
}
class component final : public component_interface
{
public:
void post_start() override
{
get_host_module();
}
void post_load() override
{
hook_module_resolving();
}
};
}
REGISTER_COMPONENT(game_module::component)

View File

@ -0,0 +1,9 @@
#pragma once
#include <utils/nt.hpp>
namespace game_module
{
utils::nt::library get_game_module();
utils::nt::library get_host_module();
}

View File

@ -0,0 +1,54 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
namespace input
{
namespace
{
utils::hook::detour cl_char_event_hook;
utils::hook::detour cl_key_event_hook;
void cl_char_event_stub(const int local_client_num, const int key)
{
if (!game_console::console_char_event(local_client_num, key))
{
return;
}
cl_char_event_hook.invoke<void>(local_client_num, key);
}
void cl_key_event_stub(const int local_client_num, const int key, const int down)
{
if (!game_console::console_key_event(local_client_num, key, down))
{
return;
}
cl_key_event_hook.invoke<void>(local_client_num, key, down);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi())
{
return;
}
cl_char_event_hook.create(SELECT_VALUE(0x000000000, 0x14024E810), cl_char_event_stub);
cl_key_event_hook.create(SELECT_VALUE(0x000000000, 0x14024EA60), cl_key_event_stub);
}
};
}
REGISTER_COMPONENT(input::component)

View File

@ -0,0 +1,58 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "localized_strings.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "game/game.hpp"
namespace localized_strings
{
namespace
{
utils::hook::detour seh_string_ed_get_string_hook;
std::unordered_map<std::string, std::string>& get_localized_overrides()
{
static std::unordered_map<std::string, std::string> overrides;
return overrides;
}
std::mutex& get_synchronization_mutex()
{
static std::mutex mutex;
return mutex;
}
const char* seh_string_ed_get_string(const char* reference)
{
std::lock_guard<std::mutex> _(get_synchronization_mutex());
auto& overrides = get_localized_overrides();
const auto entry = overrides.find(reference);
if (entry != overrides.end())
{
return utils::string::va("%s", entry->second.data());
}
return seh_string_ed_get_string_hook.invoke<const char*>(reference);
}
}
void override(const std::string& key, const std::string& value)
{
std::lock_guard<std::mutex> _(get_synchronization_mutex());
get_localized_overrides()[key] = value;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// Change some localized strings
seh_string_ed_get_string_hook.create(SELECT_VALUE(0x000000000, 0x1404BB2A0), &seh_string_ed_get_string);
}
};
}
REGISTER_COMPONENT(localized_strings::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace localized_strings
{
void override(const std::string& key, const std::string& value);
}

View File

@ -0,0 +1,279 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "command.hpp"
#include "network.hpp"
#include "game_console.hpp"
#include "../game/game.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <game/dvars.hpp>
namespace network
{
namespace
{
std::unordered_map<std::string, callback>& get_callbacks()
{
static std::unordered_map<std::string, callback> callbacks{};
return callbacks;
}
bool handle_command(game::netadr_s* address, const char* command, game::msg_t* message)
{
const auto cmd_string = utils::string::to_lower(command);
auto& callbacks = get_callbacks();
const auto handler = callbacks.find(cmd_string);
if (handler == callbacks.end())
{
return false;
}
const auto offset = cmd_string.size() + 5;
const std::string_view data(message->data + offset, message->cursize - offset);
handler->second(*address, data);
return true;
}
void handle_command_stub(utils::hook::assembler& a)
{
const auto return_unhandled = a.newLabel();
a.pushad64();
a.mov(r8, rsi); // message
a.mov(rdx, rdi); // command
a.mov(rcx, r14); // netaddr
a.call_aligned(handle_command);
a.test(al, al);
a.jz(return_unhandled);
// Command handled
a.popad64();
a.mov(al, 1);
a.jmp(0x14020AA10);
a.bind(return_unhandled);
a.popad64();
a.jmp(0x14020A19A);
}
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2)
{
if (a1->type == a2->type)
{
switch (a1->type)
{
case game::netadrtype_t::NA_BOT:
case game::netadrtype_t::NA_LOOPBACK:
return a1->port == a2->port;
case game::netadrtype_t::NA_IP:
return !memcmp(a1->ip, a2->ip, 4);
case game::netadrtype_t::NA_BROADCAST:
return true;
default:
break;
}
}
return false;
}
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2)
{
return net_compare_base_address(a1, a2) && a1->port == a2->port;
}
void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*,
const char*, bool)
{
// This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid
// We don't want this to do anything. It decides to crash seemingly randomly
// Rather than try and let the player in, just tell them they are a duplicate player and reject connection
game::NET_OutOfBandPrint(game::NS_SERVER, from, "error\nYou are already connected to the server.");
}
}
void on(const std::string& command, const callback& callback)
{
get_callbacks()[utils::string::to_lower(command)] = callback;
}
void dw_send_to_stub(const unsigned int size, const char* src, game::netadr_s* a3)
{
sockaddr s = {};
game::NetadrToSockadr(a3, &s); //0x1404F62F0
sendto(*game::query_socket, src, size - 2, 0, &s, 16);
}
void send(const game::netadr_s& address, const std::string& command, const std::string& data, const char separator)
{
std::string packet = "\xFF\xFF\xFF\xFF";
packet.append(command);
packet.push_back(separator);
packet.append(data);
send_data(address, packet);
}
void send_data(const game::netadr_s& address, const std::string& data)
{
if (address.type == game::NA_LOOPBACK)
{
game::NET_SendLoopPacket(game::NS_CLIENT1, static_cast<int>(data.size()), data.data(), &address);
}
else
{
game::Sys_SendPacket(static_cast<int>(data.size()), data.data(), &address);
}
}
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b)
{
return net_compare_address(&a, &b);
}
const char* net_adr_to_string(const game::netadr_s& a)
{
if (a.type == game::netadrtype_t::NA_LOOPBACK)
{
return "loopback";
}
if (a.type == game::netadrtype_t::NA_BOT)
{
return "bot";
}
if (a.type == game::netadrtype_t::NA_IP || a.type == game::netadrtype_t::NA_BROADCAST)
{
if (a.port)
{
return utils::string::va("%u.%u.%u.%u:%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3], htons(a.port));
}
return utils::string::va("%u.%u.%u.%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3]);
}
return "bad";
}
game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags,
const char* description)
{
auto dvar = dvars::register_int("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port");
// read net_port from command line
command::read_startup_variable("net_port");
return dvar;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
{
if (game::environment::is_sp())
{
return;
}
// redirect dw_sendto to raw socket
//utils::hook::jump(0x1404D850A, reinterpret_cast<void*>(0x1404D849A));
utils::hook::call(0x140513467, dw_send_to_stub); // H1MP64(1.4)
utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub);
// intercept command handling
utils::hook::jump(0x140252327, utils::hook::assemble(handle_command_stub), true); // H1MP64(1.4)
// handle xuid without secure connection
utils::hook::nop(0x140486AAF, 6); // H1MP64(1.4)
utils::hook::jump(0x140424F20, net_compare_address); // H1MP64(1.4)
utils::hook::jump(0x140424F70, net_compare_base_address); // H1MP64(1.4)
// don't establish secure conenction
utils::hook::set<uint8_t>(0x14027EA4D, 0xEB); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x14027EB1E, 0xEB); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x14027EF8D, 0xEB); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x14025081F, 0xEB); // H1MP64(1.4)
// ignore unregistered connection
utils::hook::jump(0x140480F46, reinterpret_cast<void*>(0x140480EE5)); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x140480F3B, 0xEB); // H1MP64(1.4)
// disable xuid verification
utils::hook::set<uint8_t>(0x14005B62D, 0xEB); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x14005B649, 0xEB); // H1MP64(1.4) NOT_SURE SHOULD JZ BUT LEA
// disable xuid verification
utils::hook::nop(0x14048382C, 2); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x140483889, 0xEB); // H1MP64(1.4) NOT_SURE
// ignore configstring mismatch
utils::hook::set<uint8_t>(0x1402591C9, 0xEB); // H1MP64(1.4)
// ignore dw handle in SV_PacketEvent
utils::hook::set<uint8_t>(0x1404898E2, 0xEB); // H1MP64(1.4)
utils::hook::call(0x1404898D6, &net_compare_address); // H1MP64(1.4)
// ignore dw handle in SV_FindClientByAddress
utils::hook::set<uint8_t>(0x140488EFD, 0xEB); // H1MP64(1.4)
utils::hook::call(0x140488EF1, &net_compare_address); // H1MP64(1.4)
// ignore dw handle in SV_DirectConnect
utils::hook::set<uint8_t>(0x140480C58, 0xEB); // H1MP64(1.4)
utils::hook::set<uint8_t>(0x140480CF2, 0xEB); // H1MP64(1.4) NOT_SURE
utils::hook::call(0x140480C4B, &net_compare_address); // H1MP64(1.4)
utils::hook::call(0x140480E62, &net_compare_address); // H1MP64(1.4)
// increase cl_maxpackets
//dvars::override::Dvar_RegisterInt("cl_maxpackets", 1000, 1, 1000, 0x1);
dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, 0x1, true);
// ignore impure client
utils::hook::jump(0x140481B58, reinterpret_cast<void*>(0x140481BEE)); // H1MP64(1.4)
// don't send checksum
utils::hook::set<uint8_t>(0x140513433, 0); // H1MP64(1.4) mov: r8d, edi ; LEN
// don't read checksum
utils::hook::jump(0x140513389, 0x14051339F); // H1MP64(1.4)
// don't try to reconnect client
utils::hook::call(0x140480DFF, reconnect_migratated_client); // H1MP64(1.4)
utils::hook::nop(0x140480DDB, 4); // H1MP64(1.4) this crashes when reconnecting for some reason
// allow server owner to modify net_port before the socket bind
utils::hook::call(0x140512BE5, register_netport_stub); // H1MP64(1.4)
utils::hook::call(0x140512D20, register_netport_stub); // H1MP64(1.4)
// ignore built in "print" oob command and add in our own
utils::hook::set<uint8_t>(0x14025280E, 0xEB); // H1MP64(1.4)
on("print", [](const game::netadr_s& addr, const std::string_view& data)
{
const std::string message{ data };
if (game::environment::is_dedi())
{
printf("%s\n", message.data());
}
else
{
game_console::print(game_console::con_type_info, "%s\n", message.data());
}
});
}
}
};
}
REGISTER_COMPONENT(network::component)

View File

@ -0,0 +1,48 @@
#pragma once
#include "game/game.hpp"
namespace network
{
using callback = std::function<void(const game::netadr_s&, const std::string_view&)>;
void on(const std::string& command, const callback& callback);
void send(const game::netadr_s& address, const std::string& command, const std::string& data = {}, char separator = ' ');
void send_data(const game::netadr_s& address, const std::string& data);
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b);
const char* net_adr_to_string(const game::netadr_s& a);
}
inline bool operator==(const game::netadr_s& a, const game::netadr_s& b)
{
return network::are_addresses_equal(a, b); //
}
inline bool operator!=(const game::netadr_s& a, const game::netadr_s& b)
{
return !(a == b); //
}
namespace std
{
template <>
struct equal_to<game::netadr_s>
{
using result_type = bool;
bool operator()(const game::netadr_s& lhs, const game::netadr_s& rhs) const
{
return network::are_addresses_equal(lhs, rhs);
}
};
template <>
struct hash<game::netadr_s>
{
size_t operator()(const game::netadr_s& x) const noexcept
{
return hash<uint32_t>()(*reinterpret_cast<const uint32_t*>(&x.ip[0])) ^ hash<uint16_t>()(x.port);
}
};
}

View File

@ -0,0 +1,83 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/nt.hpp>
#include <utils/string.hpp>
#include "game_module.hpp"
namespace redirect
{
namespace
{
void launch_complementary_game(const bool singleplayer, const std::string& mode = "")
{
const auto self = game_module::get_host_module();
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(startup_info);
auto* arguments = const_cast<char*>(utils::string::va("%s%s%s", self.get_path().data(),
(singleplayer ? " -singleplayer" : " -multiplayer"),
(mode.empty() ? "" : (" +"s + mode).data())));
CreateProcessA(self.get_path().data(), arguments, nullptr, nullptr, false, NULL, nullptr, nullptr,
&startup_info, &process_info);
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE)
{
CloseHandle(process_info.hThread);
}
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE)
{
CloseHandle(process_info.hProcess);
}
}
HINSTANCE shell_execute_a(const HWND hwnd, const LPCSTR operation, const LPCSTR file, const LPCSTR parameters,
const LPCSTR directory, const INT show_cmd)
{
if (utils::string::starts_with(file, "steam://run/393080/"))
{
launch_complementary_game(true);
return HINSTANCE(33);
}
else if (utils::string::starts_with(file, "steam://run/393100/"))
{
std::string mode(file);
mode.erase(0, 20);
if (!mode.empty())
{
mode = utils::string::replace(mode, "%2b", ""); // '+'
mode = utils::string::replace(mode, "%2", " "); // ' '
}
launch_complementary_game(false, mode);
return HINSTANCE(33);
}
return ShellExecuteA(hwnd, operation, file, parameters, directory, show_cmd);
}
}
class component final : public component_interface
{
public:
void* load_import(const std::string& library, const std::string& function) override
{
if (library == "SHELL32.dll")
{
if (function == "ShellExecuteA")
{
return shell_execute_a;
}
}
return nullptr;
}
};
}
REGISTER_COMPONENT(redirect::component)

View File

@ -0,0 +1,170 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
#include <utils/string.hpp>
namespace scheduler
{
namespace
{
struct task
{
std::function<bool()> handler{};
std::chrono::milliseconds interval{};
std::chrono::high_resolution_clock::time_point last_call{};
};
using task_list = std::vector<task>;
class task_pipeline
{
public:
void add(task&& task)
{
new_callbacks_.access([&task](task_list& tasks)
{
tasks.emplace_back(std::move(task));
});
}
void execute()
{
callbacks_.access([&](task_list& tasks)
{
this->merge_callbacks();
for (auto i = tasks.begin(); i != tasks.end();)
{
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - i->last_call;
if (diff < i->interval)
{
++i;
continue;
}
i->last_call = now;
const auto res = i->handler();
if (res == cond_end)
{
i = tasks.erase(i);
}
else
{
++i;
}
}
});
}
private:
utils::concurrency::container<task_list> new_callbacks_;
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
void merge_callbacks()
{
callbacks_.access([&](task_list& tasks)
{
new_callbacks_.access([&](task_list& new_tasks)
{
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
new_tasks = {};
});
});
}
};
volatile bool kill = false;
std::thread thread;
task_pipeline pipelines[pipeline::count];
utils::hook::detour r_end_frame_hook;
utils::hook::detour g_run_frame_hook;
utils::hook::detour main_frame_hook;
void execute(const pipeline type)
{
assert(type >= 0 && type < pipeline::count);
pipelines[type].execute();
}
void r_end_frame_stub()
{
execute(pipeline::renderer);
r_end_frame_hook.invoke<void>();
}
void server_frame_stub()
{
g_run_frame_hook.invoke<void>();
execute(pipeline::server);
}
void main_frame_stub()
{
main_frame_hook.invoke<void>();
execute(pipeline::main);
}
}
void schedule(const std::function<bool()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
assert(type >= 0 && type < pipeline::count);
task task;
task.handler = callback;
task.interval = delay;
task.last_call = std::chrono::high_resolution_clock::now();
pipelines[type].add(std::move(task));
}
void loop(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return cond_continue;
}, type, delay);
}
void once(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return cond_end;
}, type, delay);
}
class component final : public component_interface
{
public:
void post_unpack() override
{
//thread = std::thread([]()
//{
// while (!kill)
// {
// execute(pipeline::async);
// std::this_thread::sleep_for(10ms);
// }
//});
r_end_frame_hook.create(0x1405FE470, scheduler::r_end_frame_stub); // H1MP64[1.4]
g_run_frame_hook.create(0x1402772D0, scheduler::server_frame_stub); // H1MP64[1.4]
main_frame_hook.create(0x1401CE8D0, scheduler::main_frame_stub); // H1MP64[1.4]
}
};
}
REGISTER_COMPONENT(scheduler::component)

View File

@ -0,0 +1,33 @@
#pragma once
namespace scheduler
{
enum pipeline
{
// Asynchronuous pipeline, disconnected from the game
async = 0,
// The game's rendering pipeline
renderer,
// The game's server thread
server,
// The game's main thread
main,
count,
};
static const bool cond_continue = false;
static const bool cond_end = true;
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void loop(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void once(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void on_game_initialized(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
}

View File

@ -0,0 +1,188 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "steam_proxy.hpp"
#include "scheduler.hpp"
#include <utils/nt.hpp>
#include <utils/flags.hpp>
#include <utils/string.hpp>
#include <utils/binary_resource.hpp>
#include "game/game.hpp"
#include "steam/interface.hpp"
#include "steam/steam.hpp"
namespace steam_proxy
{
namespace
{
utils::binary_resource runner_file(RUNNER, "runner.exe");
bool is_disabled()
{
static const auto disabled = utils::flags::has_flag("nosteam");
return disabled;
}
}
class component final : public component_interface
{
public:
void post_load() override
{
if (game::environment::is_dedi() || is_disabled())
{
return;
}
this->load_client();
this->clean_up_on_error();
#ifndef DEV_BUILD
try
{
this->start_mod("\xF0\x9F\x90\xA4" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100);
}
catch (std::exception& e)
{
printf("Steam: %s\n", e.what());
}
#endif
}
void pre_destroy() override
{
if (this->steam_client_module_)
{
if (this->steam_pipe_)
{
if (this->global_user_)
{
this->steam_client_module_.invoke<void>("Steam_ReleaseUser", this->steam_pipe_,
this->global_user_);
}
this->steam_client_module_.invoke<bool>("Steam_BReleaseSteamPipe", this->steam_pipe_);
}
}
}
const utils::nt::library& get_overlay_module() const
{
return steam_overlay_module_;
}
private:
utils::nt::library steam_client_module_{};
utils::nt::library steam_overlay_module_{};
steam::interface client_engine_ {};
steam::interface client_user_ {};
steam::interface client_utils_ {};
void* steam_pipe_ = nullptr;
void* global_user_ = nullptr;
void* load_client_engine() const
{
if (!this->steam_client_module_) return nullptr;
for (auto i = 1; i > 0; ++i)
{
std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i);
auto* const client_engine = this->steam_client_module_
.invoke<void*>("CreateInterface", name.data(), nullptr);
if (client_engine) return client_engine;
}
return nullptr;
}
void load_client()
{
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty()) return;
utils::nt::library::load(steam_path / "tier0_s64.dll");
utils::nt::library::load(steam_path / "vstdlib_s64.dll");
this->steam_overlay_module_ = utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll");
this->steam_client_module_ = utils::nt::library::load(steam_path / "steamclient64.dll");
if (!this->steam_client_module_) return;
this->client_engine_ = load_client_engine();
if (!this->client_engine_) return;
this->steam_pipe_ = this->steam_client_module_.invoke<void*>("Steam_CreateSteamPipe");
this->global_user_ = this->steam_client_module_.invoke<void*>(
"Steam_ConnectToGlobalUser", this->steam_pipe_);
this->client_user_ = this->client_engine_.invoke<void*>(8, this->steam_pipe_, this->global_user_);
// GetIClientUser
this->client_utils_ = this->client_engine_.invoke<void*>(14, this->steam_pipe_); // GetIClientUtils
}
void start_mod(const std::string& title, size_t app_id)
{
if (!this->client_utils_ || !this->client_user_) return;
if (!this->client_user_.invoke<bool>("BIsSubscribedApp", app_id))
{
app_id = 480; // Spacewar
}
this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
char our_directory[MAX_PATH] = { 0 };
GetCurrentDirectoryA(sizeof(our_directory), our_directory);
const auto path = runner_file.get_extracted_file();
const std::string cmdline = utils::string::va("\"%s\" -proc %d", path.data(), GetCurrentProcessId());
steam::game_id game_id;
game_id.raw.type = 1; // k_EGameIDTypeGameMod
game_id.raw.app_id = app_id & 0xFFFFFF;
const auto* mod_id = "H1-Mod";
game_id.raw.mod_id = *reinterpret_cast<const unsigned int*>(mod_id) | 0x80000000;
this->client_user_.invoke<bool>("SpawnProcess", path.data(), cmdline.data(), our_directory,
&game_id.bits, title.data(), 0, 0, 0);
}
void clean_up_on_error()
{
scheduler::schedule([this]()
{
if (this->steam_client_module_
&& this->steam_pipe_
&& this->global_user_
&& this->steam_client_module_.invoke<bool>("Steam_BConnected", this->global_user_,
this->steam_pipe_)
&& this->steam_client_module_.invoke<bool>("Steam_BLoggedOn", this->global_user_, this->steam_pipe_)
)
{
return scheduler::cond_continue;
}
this->client_engine_ = nullptr;
this->client_user_ = nullptr;
this->client_utils_ = nullptr;
this->steam_pipe_ = nullptr;
this->global_user_ = nullptr;
this->steam_client_module_ = utils::nt::library{ nullptr };
return scheduler::cond_end;
});
}
};
const utils::nt::library& get_overlay_module()
{
// TODO: Find a better way to do this
return component_loader::get<component>()->get_overlay_module();
}
}
REGISTER_COMPONENT(steam_proxy::component)

View File

@ -0,0 +1,7 @@
#pragma once
#include <utils/nt.hpp>
namespace steam_proxy
{
const utils::nt::library& get_overlay_module();
}

View File

@ -0,0 +1,182 @@
#include <std_include.hpp>
#include "bit_buffer.hpp"
namespace demonware
{
bool bit_buffer::read_bytes(const unsigned int bytes, unsigned char* output)
{
return this->read(bytes * 8, output);
}
bool bit_buffer::read_bool(bool* output)
{
if (!this->read_data_type(1))
{
return false;
}
return this->read(1, output);
}
bool bit_buffer::read_uint32(unsigned int* output)
{
if (!this->read_data_type(8))
{
return false;
}
return this->read(32, output);
}
bool bit_buffer::read_data_type(const char expected)
{
char data_type = 0;
if (!this->use_data_types_) return true;
if (this->read(5, &data_type))
{
return (data_type == expected);
}
return false;
}
bool bit_buffer::write_bytes(const unsigned int bytes, const char* data)
{
return this->write_bytes(bytes, reinterpret_cast<const unsigned char*>(data));
}
bool bit_buffer::write_bytes(const unsigned int bytes, const unsigned char* data)
{
return this->write(bytes * 8, data);
}
bool bit_buffer::write_bool(bool data)
{
if (this->write_data_type(1))
{
return this->write(1, &data);
}
return false;
}
bool bit_buffer::write_int32(int data)
{
if (this->write_data_type(7))
{
return this->write(32, &data);
}
return false;
}
bool bit_buffer::write_uint32(unsigned int data)
{
if (this->write_data_type(8))
{
return this->write(32, &data);
}
return false;
}
bool bit_buffer::write_data_type(char data)
{
if (!this->use_data_types_)
{
return true;
}
return this->write(5, &data);
}
bool bit_buffer::read(unsigned int bits, void* output)
{
if (bits == 0) return false;
if ((this->current_bit_ + bits) > (this->buffer_.size() * 8)) return false;
int cur_byte = this->current_bit_ >> 3;
auto cur_out = 0;
const char* bytes = this->buffer_.data();
const auto output_bytes = static_cast<unsigned char*>(output);
while (bits > 0)
{
const int min_bit = (bits < 8) ? bits : 8;
const auto this_byte = bytes[cur_byte++] & 0xFF;
const int remain = this->current_bit_ & 7;
if ((min_bit + remain) <= 8)
{
output_bytes[cur_out] = BYTE((0xFF >> (8 - min_bit)) & (this_byte >> remain));
}
else
{
output_bytes[cur_out] = BYTE(
(0xFF >> (8 - min_bit)) & (bytes[cur_byte] << (8 - remain)) | (this_byte >> remain));
}
cur_out++;
this->current_bit_ += min_bit;
bits -= min_bit;
}
return true;
}
bool bit_buffer::write(const unsigned int bits, const void* data)
{
if (bits == 0) return false;
this->buffer_.resize(this->buffer_.size() + (bits >> 3) + 1);
int bit = bits;
const auto bytes = const_cast<char*>(this->buffer_.data());
const auto* input_bytes = static_cast<const unsigned char*>(data);
while (bit > 0)
{
const int bit_pos = this->current_bit_ & 7;
auto rem_bit = 8 - bit_pos;
const auto this_write = (bit < rem_bit) ? bit : rem_bit;
const BYTE mask = ((0xFF >> rem_bit) | (0xFF << (bit_pos + this_write)));
const int byte_pos = this->current_bit_ >> 3;
const BYTE temp_byte = (mask & bytes[byte_pos]);
const BYTE this_bit = ((bits - bit) & 7);
const auto this_byte = (bits - bit) >> 3;
auto this_data = input_bytes[this_byte];
const auto next_byte = (((bits - 1) >> 3) > this_byte) ? input_bytes[this_byte + 1] : 0;
this_data = BYTE((next_byte << (8 - this_bit)) | (this_data >> this_bit));
const BYTE out_byte = (~mask & (this_data << bit_pos) | temp_byte);
bytes[byte_pos] = out_byte;
this->current_bit_ += this_write;
bit -= this_write;
}
return true;
}
void bit_buffer::set_use_data_types(const bool use_data_types)
{
this->use_data_types_ = use_data_types;
}
unsigned int bit_buffer::size() const
{
return this->current_bit_ / 8 + (this->current_bit_ % 8 ? 1 : 0);
}
std::string& bit_buffer::get_buffer()
{
this->buffer_.resize(this->size());
return this->buffer_;
}
}

View File

@ -0,0 +1,40 @@
#pragma once
namespace demonware
{
class bit_buffer final
{
public:
bit_buffer() = default;
explicit bit_buffer(std::string buffer) : buffer_(std::move(buffer))
{
}
bool read_bytes(unsigned int bytes, unsigned char* output);
bool read_bool(bool* output);
bool read_uint32(unsigned int* output);
bool read_data_type(char expected);
bool write_bytes(unsigned int bytes, const char* data);
bool write_bytes(unsigned int bytes, const unsigned char* data);
bool write_bool(bool data);
bool write_int32(int data);
bool write_uint32(unsigned int data);
bool write_data_type(char data);
bool read(unsigned int bits, void* output);
bool write(unsigned int bits, const void* data);
void set_use_data_types(bool use_data_types);
unsigned int size() const;
std::string& get_buffer();
private:
std::string buffer_{};
unsigned int current_bit_ = 0;
bool use_data_types_ = true;
};
}

View File

@ -0,0 +1,308 @@
#include <std_include.hpp>
#include "byte_buffer.hpp"
namespace demonware
{
bool byte_buffer::read_byte(unsigned char* output)
{
if (!this->read_data_type(3)) return false;
return this->read(1, output);
}
bool byte_buffer::read_bool(bool* output)
{
if (!this->read_data_type(1)) return false;
return this->read(1, output);
}
bool byte_buffer::read_int16(short* output)
{
if (!this->read_data_type(5)) return false;
return this->read(2, output);
}
bool byte_buffer::read_uint16(unsigned short* output)
{
if (!this->read_data_type(6)) return false;
return this->read(2, output);
}
bool byte_buffer::read_int32(int* output)
{
if (!this->read_data_type(7)) return false;
return this->read(4, output);
}
bool byte_buffer::read_uint32(unsigned int* output)
{
if (!this->read_data_type(8)) return false;
return this->read(4, output);
}
bool byte_buffer::read_int64(__int64* output)
{
if (!this->read_data_type(9)) return false;
return this->read(8, output);
}
bool byte_buffer::read_uint64(unsigned __int64* output)
{
if (!this->read_data_type(10)) return false;
return this->read(8, output);
}
bool byte_buffer::read_float(float* output)
{
if (!this->read_data_type(13)) return false;
return this->read(4, output);
}
bool byte_buffer::read_string(std::string* output)
{
char* out_data;
if (this->read_string(&out_data))
{
output->clear();
output->append(out_data);
return true;
}
return false;
}
bool byte_buffer::read_string(char** output)
{
if (!this->read_data_type(16)) return false;
*output = const_cast<char*>(this->buffer_.data()) + this->current_byte_;
this->current_byte_ += strlen(*output) + 1;
return true;
}
bool byte_buffer::read_string(char* output, const int length)
{
if (!this->read_data_type(16)) return false;
strcpy_s(output, length, const_cast<char*>(this->buffer_.data()) + this->current_byte_);
this->current_byte_ += strlen(output) + 1;
return true;
}
bool byte_buffer::read_blob(std::string* output)
{
char* out_data;
int length;
if (this->read_blob(&out_data, &length))
{
output->clear();
output->append(out_data, length);
return true;
}
return false;
}
bool byte_buffer::read_blob(char** output, int* length)
{
if (!this->read_data_type(0x13))
{
return false;
}
unsigned int size;
this->read_uint32(&size);
*output = const_cast<char*>(this->buffer_.data()) + this->current_byte_;
*length = static_cast<int>(size);
this->current_byte_ += size;
return true;
}
bool byte_buffer::read_data_type(const char expected)
{
if (!this->use_data_types_) return true;
char type;
this->read(1, &type);
return type == expected;
}
bool byte_buffer::read_array_header(const unsigned char expected, unsigned int* element_count,
unsigned int* element_size)
{
if (element_count) *element_count = 0;
if (element_size) *element_size = 0;
if (!this->read_data_type(expected + 100)) return false;
uint32_t array_size, el_count;
if (!this->read_uint32(&array_size)) return false;
this->set_use_data_types(false);
this->read_uint32(&el_count);
this->set_use_data_types(true);
if (element_count) *element_count = el_count;
if (element_size) *element_size = array_size / el_count;
return true;
}
bool byte_buffer::write_byte(char data)
{
this->write_data_type(3);
return this->write(1, &data);
}
bool byte_buffer::write_bool(bool data)
{
this->write_data_type(1);
return this->write(1, &data);
}
bool byte_buffer::write_int16(short data)
{
this->write_data_type(5);
return this->write(2, &data);
}
bool byte_buffer::write_uint16(unsigned short data)
{
this->write_data_type(6);
return this->write(2, &data);
}
bool byte_buffer::write_int32(int data)
{
this->write_data_type(7);
return this->write(4, &data);
}
bool byte_buffer::write_uint32(unsigned int data)
{
this->write_data_type(8);
return this->write(4, &data);
}
bool byte_buffer::write_int64(__int64 data)
{
this->write_data_type(9);
return this->write(8, &data);
}
bool byte_buffer::write_uint64(unsigned __int64 data)
{
this->write_data_type(10);
return this->write(8, &data);
}
bool byte_buffer::write_data_type(char data)
{
if (!this->use_data_types_) return true;
return this->write(1, &data);
}
bool byte_buffer::write_float(float data)
{
this->write_data_type(13);
return this->write(4, &data);
}
bool byte_buffer::write_string(const std::string& data)
{
return this->write_string(data.data());
}
bool byte_buffer::write_string(const char* data)
{
this->write_data_type(16);
return this->write(static_cast<int>(strlen(data)) + 1, data);
}
bool byte_buffer::write_blob(const std::string& data)
{
return this->write_blob(data.data(), INT(data.size()));
}
bool byte_buffer::write_blob(const char* data, const int length)
{
this->write_data_type(0x13);
this->write_uint32(length);
return this->write(length, data);
}
bool byte_buffer::write_array_header(const unsigned char type, const unsigned int element_count,
const unsigned int element_size)
{
const auto using_types = this->is_using_data_types();
this->set_use_data_types(false);
auto result = this->write_byte(type + 100);
this->set_use_data_types(true);
result &= this->write_uint32(element_count * element_size);
this->set_use_data_types(false);
result &= this->write_uint32(element_count);
this->set_use_data_types(using_types);
return result;
}
bool byte_buffer::read(const int bytes, void* output)
{
if (bytes + this->current_byte_ > this->buffer_.size()) return false;
std::memmove(output, this->buffer_.data() + this->current_byte_, bytes);
this->current_byte_ += bytes;
return true;
}
bool byte_buffer::write(const int bytes, const void* data)
{
this->buffer_.append(static_cast<const char*>(data), bytes);
this->current_byte_ += bytes;
return true;
}
bool byte_buffer::write(const std::string& data)
{
return this->write(static_cast<int>(data.size()), data.data());
}
void byte_buffer::set_use_data_types(const bool use_data_types)
{
this->use_data_types_ = use_data_types;
}
size_t byte_buffer::size() const
{
return this->buffer_.size();
}
bool byte_buffer::is_using_data_types() const
{
return use_data_types_;
}
std::string& byte_buffer::get_buffer()
{
return this->buffer_;
}
std::string byte_buffer::get_remaining()
{
return std::string(this->buffer_.begin() + this->current_byte_, this->buffer_.end());
}
bool byte_buffer::has_more_data() const
{
return this->buffer_.size() > this->current_byte_;
}
}

View File

@ -0,0 +1,69 @@
#pragma once
namespace demonware
{
class byte_buffer final
{
public:
byte_buffer() = default;
explicit byte_buffer(std::string buffer) : buffer_(std::move(buffer))
{
}
bool read_byte(unsigned char* output);
bool read_bool(bool* output);
bool read_int16(short* output);
bool read_uint16(unsigned short* output);
bool read_int32(int* output);
bool read_uint32(unsigned int* output);
bool read_int64(__int64* output);
bool read_uint64(unsigned __int64* output);
bool read_float(float* output);
bool read_string(char** output);
bool read_string(char* output, int length);
bool read_string(std::string* output);
bool read_blob(char** output, int* length);
bool read_blob(std::string* output);
bool read_data_type(char expected);
bool read_array_header(unsigned char expected, unsigned int* element_count,
unsigned int* element_size = nullptr);
bool write_byte(char data);
bool write_bool(bool data);
bool write_int16(short data);
bool write_uint16(unsigned short data);
bool write_int32(int data);
bool write_uint32(unsigned int data);
bool write_int64(__int64 data);
bool write_uint64(unsigned __int64 data);
bool write_data_type(char data);
bool write_float(float data);
bool write_string(const char* data);
bool write_string(const std::string& data);
bool write_blob(const char* data, int length);
bool write_blob(const std::string& data);
bool write_array_header(unsigned char type, unsigned int element_count, unsigned int element_size);
bool read(int bytes, void* output);
bool write(int bytes, const void* data);
bool write(const std::string& data);
void set_use_data_types(bool use_data_types);
size_t size() const;
bool is_using_data_types() const;
std::string& get_buffer();
std::string get_remaining();
bool has_more_data() const;
private:
std::string buffer_;
size_t current_byte_ = 0;
bool use_data_types_ = true;
};
}

View File

@ -0,0 +1,144 @@
#pragma once
#include "byte_buffer.hpp"
namespace demonware
{
class bdTaskResult
{
public:
virtual ~bdTaskResult() = default;
virtual void serialize(byte_buffer*)
{
}
virtual void deserialize(byte_buffer*)
{
}
};
class bdFileData final : public bdTaskResult
{
public:
std::string file_data;
explicit bdFileData(std::string buffer) : file_data(std::move(buffer))
{
}
void serialize(byte_buffer* buffer) override
{
buffer->write_blob(this->file_data);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_blob(&this->file_data);
}
};
class bdFileInfo final : public bdTaskResult
{
public:
uint64_t file_id;
uint32_t create_time;
uint32_t modified_time;
bool priv;
uint64_t owner_id;
std::string filename;
uint32_t file_size;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint32(this->file_size);
buffer->write_uint64(this->file_id);
buffer->write_uint32(this->create_time);
buffer->write_uint32(this->modified_time);
buffer->write_bool(this->priv);
buffer->write_uint64(this->owner_id);
buffer->write_string(this->filename);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint32(&this->file_size);
buffer->read_uint64(&this->file_id);
buffer->read_uint32(&this->create_time);
buffer->read_uint32(&this->modified_time);
buffer->read_bool(&this->priv);
buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->filename);
}
};
class bdTimeStamp final : public bdTaskResult
{
public:
uint32_t unix_time;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint32(this->unix_time);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint32(&this->unix_time);
}
};
class bdDMLInfo : public bdTaskResult
{
public:
std::string country_code; // Char [3]
std::string country; // Char [65]
std::string region; // Char [65]
std::string city; // Char [129]
float latitude;
float longitude;
void serialize(byte_buffer* buffer) override
{
buffer->write_string(this->country_code);
buffer->write_string(this->country);
buffer->write_string(this->region);
buffer->write_string(this->city);
buffer->write_float(this->latitude);
buffer->write_float(this->longitude);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_string(&this->country_code);
buffer->read_string(&this->country);
buffer->read_string(&this->region);
buffer->read_string(&this->city);
buffer->read_float(&this->latitude);
buffer->read_float(&this->longitude);
}
};
class bdDMLRawData final : public bdDMLInfo
{
public:
uint32_t asn; // Autonomous System Number.
std::string timezone;
void serialize(byte_buffer* buffer) override
{
bdDMLInfo::serialize(buffer);
buffer->write_uint32(this->asn);
buffer->write_string(this->timezone);
}
void deserialize(byte_buffer* buffer) override
{
bdDMLInfo::deserialize(buffer);
buffer->read_uint32(&this->asn);
buffer->read_string(&this->timezone);
}
};
}

View File

@ -0,0 +1,130 @@
#include <std_include.hpp>
#include "keys.hpp"
#include <utils/cryptography.hpp>
#include <utils/string.hpp>
namespace demonware
{
struct data_t
{
char m_session_key[24];
char m_response[8];
char m_hmac_key[20];
char m_enc_key[16];
char m_dec_key[16];
} data{};
std::string packet_buffer;
void calculate_hmacs_s1(const char* data_, const unsigned int data_size, const char* key,
const unsigned int key_size,
char* dst, const unsigned int dst_size)
{
char buffer[64];
unsigned int pos = 0;
unsigned int out_offset = 0;
char count = 1;
std::string result;
// buffer add key
std::memcpy(&buffer[pos], key, key_size);
pos += key_size;
// buffer add count
buffer[pos] = count;
pos++;
// calculate hmac
result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(data_, data_size));
// save output
std::memcpy(dst, result.data(), std::min(20u, (dst_size - out_offset)));
out_offset = 20;
// second loop
while (true)
{
// if we filled the output buffer, exit
if (out_offset >= dst_size)
break;
// buffer add last result
pos = 0;
std::memcpy(&buffer[pos], result.data(), 20);
pos += 20;
// buffer add key
std::memcpy(&buffer[pos], key, key_size);
pos += key_size;
// buffer add count
count++;
buffer[pos] = count;
pos++;
// calculate hmac
result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(data_, data_size));
// save output
std::memcpy(dst + out_offset, result.data(), std::min(20u, (dst_size - out_offset)));
out_offset += 20;
}
}
void derive_keys_s1()
{
const auto out_1 = utils::cryptography::sha1::compute(packet_buffer); // out_1 size 20
auto data_3 = utils::cryptography::hmac_sha1::compute(data.m_session_key, out_1);
char out_2[16];
calculate_hmacs_s1(data_3.data(), 20, "CLIENTCHAL", 10, out_2, 16);
char out_3[72];
calculate_hmacs_s1(data_3.data(), 20, "BDDATA", 6, out_3, 72);
std::memcpy(data.m_response, &out_2[8], 8);
std::memcpy(data.m_hmac_key, &out_3[20], 20);
std::memcpy(data.m_dec_key, &out_3[40], 16);
std::memcpy(data.m_enc_key, &out_3[56], 16);
#ifdef DEBUG
printf("[DW] Response id: %s\n", utils::string::dump_hex(std::string(&out_2[8], 8)).data());
printf("[DW] Hash verify: %s\n", utils::string::dump_hex(std::string(&out_3[20], 20)).data());
printf("[DW] AES dec key: %s\n", utils::string::dump_hex(std::string(&out_3[40], 16)).data());
printf("[DW] AES enc key: %s\n", utils::string::dump_hex(std::string(&out_3[56], 16)).data());
printf("[DW] Bravo 6, going dark.\n");
#endif
}
void queue_packet_to_hash(const std::string& packet)
{
packet_buffer.append(packet);
}
void set_session_key(const std::string& key)
{
std::memcpy(data.m_session_key, key.data(), 24);
}
std::string get_decrypt_key()
{
return std::string(data.m_dec_key, 16);
}
std::string get_encrypt_key()
{
return std::string(data.m_enc_key, 16);
}
std::string get_hmac_key()
{
return std::string(data.m_hmac_key, 20);
}
std::string get_response_id()
{
return std::string(data.m_response, 8);
}
}

View File

@ -0,0 +1,12 @@
#pragma once
namespace demonware
{
void derive_keys_s1();
void queue_packet_to_hash(const std::string& packet);
void set_session_key(const std::string& key);
std::string get_decrypt_key();
std::string get_encrypt_key();
std::string get_hmac_key();
std::string get_response_id();
}

View File

@ -0,0 +1,87 @@
#include <std_include.hpp>
#include "keys.hpp"
#include "reply.hpp"
#include "servers/service_server.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
std::string unencrypted_reply::data()
{
byte_buffer result;
result.set_use_data_types(false);
result.write_int32(static_cast<int>(this->buffer_.size()) + 2);
result.write_bool(false);
result.write_byte(this->type());
result.write(this->buffer_);
return result.get_buffer();
}
std::string encrypted_reply::data()
{
byte_buffer result;
result.set_use_data_types(false);
byte_buffer enc_buffer;
enc_buffer.set_use_data_types(false);
enc_buffer.write_uint32(static_cast<unsigned int>(this->buffer_.size())); // service data size CHECKTHIS!!
enc_buffer.write_byte(this->type()); // TASK_REPLY type
enc_buffer.write(this->buffer_); // service data
auto aligned_data = enc_buffer.get_buffer();
auto size = aligned_data.size();
size = ~15 & (size + 15); // 16 byte align
aligned_data.resize(size);
// seed
std::string seed("\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED", 16);
// encrypt
const auto enc_data = utils::cryptography::aes::encrypt(aligned_data, seed, demonware::get_encrypt_key());
// header : encrypted service data : hash
static auto msg_count = 0;
msg_count++;
byte_buffer response;
response.set_use_data_types(false);
response.write_int32(30 + static_cast<int>(enc_data.size()));
response.write_byte(static_cast<char>(0xAB));
response.write_byte(static_cast<char>(0x85));
response.write_int32(msg_count);
response.write(16, seed.data());
response.write(enc_data);
// hash entire packet and append end
auto hash_data = utils::cryptography::hmac_sha1::compute(response.get_buffer(), demonware::get_hmac_key());
hash_data.resize(8);
response.write(8, hash_data.data());
return response.get_buffer();
}
void remote_reply::send(bit_buffer* buffer, const bool encrypted)
{
std::unique_ptr<typed_reply> reply;
if (encrypted) reply = std::make_unique<encrypted_reply>(this->type_, buffer);
else reply = std::make_unique<unencrypted_reply>(this->type_, buffer);
this->server_->send_reply(reply.get());
}
void remote_reply::send(byte_buffer* buffer, const bool encrypted)
{
std::unique_ptr<typed_reply> reply;
if (encrypted) reply = std::make_unique<encrypted_reply>(this->type_, buffer);
else reply = std::make_unique<unencrypted_reply>(this->type_, buffer);
this->server_->send_reply(reply.get());
}
}

View File

@ -0,0 +1,164 @@
#pragma once
#include "bit_buffer.hpp"
#include "byte_buffer.hpp"
#include "data_types.hpp"
namespace demonware
{
class reply
{
public:
reply() = default;
reply(reply&&) = delete;
reply(const reply&) = delete;
reply& operator=(reply&&) = delete;
reply& operator=(const reply&) = delete;
virtual ~reply() = default;
virtual std::string data() = 0;
};
class raw_reply : public reply
{
protected:
std::string buffer_;
public:
raw_reply() = default;
explicit raw_reply(std::string data) : buffer_(std::move(data))
{
}
std::string data() override
{
return this->buffer_;
}
};
class typed_reply : public raw_reply
{
public:
typed_reply(const uint8_t _type) : type_(_type)
{
}
protected:
uint8_t type() const { return this->type_; }
private:
uint8_t type_;
};
class encrypted_reply final : public typed_reply
{
public:
encrypted_reply(const uint8_t type, bit_buffer* bbuffer) : typed_reply(type)
{
this->buffer_.append(bbuffer->get_buffer());
}
encrypted_reply(const uint8_t type, byte_buffer* bbuffer) : typed_reply(type)
{
this->buffer_.append(bbuffer->get_buffer());
}
std::string data() override;
};
class unencrypted_reply final : public typed_reply
{
public:
unencrypted_reply(const uint8_t _type, bit_buffer* bbuffer) : typed_reply(_type)
{
this->buffer_.append(bbuffer->get_buffer());
}
unencrypted_reply(const uint8_t _type, byte_buffer* bbuffer) : typed_reply(_type)
{
this->buffer_.append(bbuffer->get_buffer());
}
std::string data() override;
};
class service_server;
class remote_reply final
{
public:
remote_reply(service_server* server, uint8_t _type) : type_(_type), server_(server)
{
}
void send(bit_buffer* buffer, bool encrypted);
void send(byte_buffer* buffer, bool encrypted);
uint8_t type() const { return this->type_; }
private:
uint8_t type_;
service_server* server_;
};
class service_reply final
{
public:
service_reply(service_server* _server, const uint8_t _type, const uint32_t _error)
: type_(_type), error_(_error), reply_(_server, 1)
{
}
uint64_t send()
{
static uint64_t id = 0x0000000000000000;
const auto transaction_id = ++id;
byte_buffer buffer;
buffer.write_uint64(transaction_id);
buffer.write_uint32(this->error_);
buffer.write_byte(this->type_);
if (!this->error_)
{
buffer.write_uint32(uint32_t(this->objects_.size()));
if (!this->objects_.empty())
{
buffer.write_uint32(uint32_t(this->objects_.size()));
for (auto& object : this->objects_)
{
object->serialize(&buffer);
}
this->objects_.clear();
}
}
else
{
buffer.write_uint64(transaction_id);
}
this->reply_.send(&buffer, true);
return transaction_id;
}
void add(const std::shared_ptr<bdTaskResult>& object)
{
this->objects_.push_back(object);
}
void add(bdTaskResult* object)
{
this->add(std::shared_ptr<bdTaskResult>(object));
}
private:
uint8_t type_;
uint32_t error_;
remote_reply reply_;
std::vector<std::shared_ptr<bdTaskResult>> objects_;
};
}

View File

@ -0,0 +1,60 @@
#pragma once
#include "servers/base_server.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
template <typename T>
class server_registry
{
static_assert(std::is_base_of<base_server, T>::value, "Invalid server registry type");
public:
template <typename S, typename ...Args>
void create(Args&&... args)
{
static_assert(std::is_base_of<T, S>::value, "Invalid server type");
auto server = std::make_unique<S>(std::forward<Args>(args)...);
const auto address = server->get_address();
servers_[address] = std::move(server);
}
void for_each(const std::function<void(T&)>& callback) const
{
for (auto& server : servers_)
{
callback(*server.second);
}
}
T* find(const std::string& name)
{
const auto address = utils::cryptography::jenkins_one_at_a_time::compute(name);
return find(address);
}
T* find(const uint32_t address)
{
const auto it = servers_.find(address);
if (it == servers_.end())
{
return nullptr;
}
return it->second.get();
}
void frame()
{
for (auto& server : servers_)
{
server.second->frame();
}
}
private:
std::unordered_map<uint32_t, std::unique_ptr<T>> servers_;
};
}

View File

@ -0,0 +1,162 @@
#include <std_include.hpp>
#include "auth3_server.hpp"
#include "../keys.hpp"
#include <utils/cryptography.hpp>
#include <utils/string.hpp>
namespace demonware
{
namespace
{
#pragma pack(push, 1)
struct auth_ticket
{
unsigned int m_magicNumber;
char m_type;
unsigned int m_titleID;
unsigned int m_timeIssued;
unsigned int m_timeExpires;
unsigned __int64 m_licenseID;
unsigned __int64 m_userID;
char m_username[64];
char m_sessionKey[24];
char m_usingHashMagicNumber[3];
char m_hash[4];
};
#pragma pack(pop)
}
void auth3_server::send_reply(reply* data)
{
if (!data) return;
this->send(data->data());
}
void auth3_server::handle(const std::string& packet)
{
if (packet.starts_with("POST /auth/"))
{
#ifdef DEBUG
printf("[DW]: [auth]: user requested authentication.\n");
#endif
return;
}
unsigned int title_id = 0;
unsigned int iv_seed = 0;
std::string identity{};
std::string token{};
rapidjson::Document j;
j.Parse(packet.data(), packet.size());
if (j.HasMember("title_id") && j["title_id"].IsString())
{
title_id = std::stoul(j["title_id"].GetString());
}
if (j.HasMember("iv_seed") && j["iv_seed"].IsString())
{
iv_seed = std::stoul(j["iv_seed"].GetString());
}
if (j.HasMember("extra_data") && j["extra_data"].IsString())
{
rapidjson::Document extra_data;
auto& ed = j["extra_data"];
extra_data.Parse(ed.GetString(), ed.GetStringLength());
if (extra_data.HasMember("token") && extra_data["token"].IsString())
{
auto& token_field = extra_data["token"];
std::string token_b64(token_field.GetString(), token_field.GetStringLength());
token = utils::cryptography::base64::decode(token_b64);
}
}
#ifdef DEBUG
printf("[DW]: [auth]: authenticating user %s\n", token.data() + 64);
#endif
std::string auth_key(reinterpret_cast<char*>(token.data() + 32), 24);
std::string session_key(
"\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37", 24);
// client_ticket
auth_ticket ticket{};
std::memset(&ticket, 0x0, sizeof ticket);
ticket.m_magicNumber = 0x0EFBDADDE;
ticket.m_type = 0;
ticket.m_titleID = title_id;
ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
ticket.m_timeExpires = ticket.m_timeIssued + 30000;
ticket.m_licenseID = 0;
ticket.m_userID = reinterpret_cast<uint64_t>(token.data() + 56);
strncpy_s(ticket.m_username, sizeof(ticket.m_username), reinterpret_cast<char*>(token.data() + 64), 64);
std::memcpy(ticket.m_sessionKey, session_key.data(), 24);
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&iv_seed), 4));
const auto ticket_enc = utils::cryptography::des3::encrypt(
std::string(reinterpret_cast<char*>(&ticket), sizeof(ticket)), iv, auth_key);
const auto ticket_b64 = utils::cryptography::base64::encode(
reinterpret_cast<const unsigned char*>(ticket_enc.data()), 128);
// server_ticket
uint8_t auth_data[128];
std::memset(&auth_data, 0, sizeof auth_data);
std::memcpy(auth_data, session_key.data(), 24);
const auto auth_data_b64 = utils::cryptography::base64::encode(auth_data, 128);
demonware::set_session_key(session_key);
// header time
char date[64];
const auto now = time(nullptr);
tm gmtm{};
gmtime_s(&gmtm, &now);
strftime(date, 64, "%a, %d %b %G %T", &gmtm);
// json content
rapidjson::Document doc;
doc.SetObject();
doc.AddMember("auth_task", "29", doc.GetAllocator());
doc.AddMember("code", "700", doc.GetAllocator());
auto seed = std::to_string(iv_seed);
doc.AddMember("iv_seed", rapidjson::StringRef(seed.data(), seed.size()), doc.GetAllocator());
doc.AddMember("client_ticket", rapidjson::StringRef(ticket_b64.data(), ticket_b64.size()), doc.GetAllocator());
doc.AddMember("server_ticket", rapidjson::StringRef(auth_data_b64.data(), auth_data_b64.size()),
doc.GetAllocator());
doc.AddMember("client_id", "", doc.GetAllocator());
doc.AddMember("account_type", "steam", doc.GetAllocator());
doc.AddMember("crossplay_enabled", false, doc.GetAllocator());
doc.AddMember("loginqueue_eanbled", false, doc.GetAllocator());
rapidjson::Value value{};
doc.AddMember("lsg_endpoint", value, doc.GetAllocator());
rapidjson::StringBuffer buffer{};
rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::ASCII<>>
writer(buffer);
doc.Accept(writer);
// http stuff
std::string result;
result.append("HTTP/1.1 200 OK\r\n");
result.append("Server: TornadoServer/4.5.3\r\n");
result.append("Content-Type: application/json\r\n");
result.append(utils::string::va("Date: %s GMT\r\n", date));
result.append(utils::string::va("Content-Length: %d\r\n\r\n", buffer.GetLength()));
result.append(buffer.GetString(), buffer.GetLength());
raw_reply reply(result);
this->send_reply(&reply);
#ifdef DEBUG
printf("[DW]: [auth]: user successfully authenticated.\n");
#endif
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "tcp_server.hpp"
#include "../reply.hpp"
namespace demonware
{
class auth3_server : public tcp_server
{
public:
using tcp_server::tcp_server;
private:
void send_reply(reply* data);
void handle(const std::string& packet) override;
};
}

View File

@ -0,0 +1,22 @@
#include <std_include.hpp>
#include "base_server.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
base_server::base_server(std::string name): name_(std::move(name))
{
this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_);
}
const std::string& base_server::get_name() const
{
return this->name_;
}
uint32_t base_server::get_address() const
{
return this->address_;
}
}

View File

@ -0,0 +1,30 @@
#pragma once
namespace demonware
{
class base_server
{
public:
using stream_queue = std::queue<char>;
using data_queue = std::queue<std::string>;
base_server(std::string name);
base_server(base_server&&) = delete;
base_server(const base_server&) = delete;
base_server& operator=(base_server&&) = delete;
base_server& operator=(const base_server&) = delete;
virtual ~base_server() = default;
const std::string& get_name() const;
uint32_t get_address() const;
virtual void frame() = 0;
private:
std::string name_;
std::uint32_t address_ = 0;
};
}

View File

@ -0,0 +1,177 @@
#include <std_include.hpp>
#include "lobby_server.hpp"
#include "../services.hpp"
#include "../keys.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
lobby_server::lobby_server(std::string name) : tcp_server(std::move(name))
{
this->register_service<bdAnticheat>();
this->register_service<bdBandwidthTest>();
this->register_service<bdContentStreaming>();
this->register_service<bdDML>();
this->register_service<bdEventLog>();
this->register_service<bdGroups>();
this->register_service<bdStats>();
this->register_service<bdStorage>();
this->register_service<bdTitleUtilities>();
this->register_service<bdProfiles>();
this->register_service<bdRichPresence>();
this->register_service<bdFacebook>();
this->register_service<bdUNK63>();
this->register_service<bdUNK80>();
this->register_service<bdPresence>();
this->register_service<bdMarketingComms>();
this->register_service<bdMatchMaking2>();
this->register_service<bdMarketing>();
};
void lobby_server::send_reply(reply* data)
{
if (!data) return;
this->send(data->data());
}
void lobby_server::handle(const std::string& packet)
{
byte_buffer buffer(packet);
buffer.set_use_data_types(false);
try
{
while (buffer.has_more_data())
{
int size;
buffer.read_int32(&size);
if (size <= 0)
{
const std::string zero("\x00\x00\x00\x00", 4);
raw_reply reply(zero);
this->send_reply(&reply);
return;
}
else if (size == 0xC8)
{
#ifdef DEBUG
printf("[DW]: [lobby]: received client_header_ack.\n");
#endif
int c8;
buffer.read_int32(&c8);
std::string packet_1 = buffer.get_remaining();
demonware::queue_packet_to_hash(packet_1);
const std::string packet_2(
"\x16\x00\x00\x00\xab\x81\xd2\x00\x00\x00\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37",
26);
demonware::queue_packet_to_hash(packet_2);
raw_reply reply(packet_2);
this->send_reply(&reply);
#ifdef DEBUG
printf("[DW]: [lobby]: sending server_header_ack.\n");
#endif
return;
}
if (buffer.size() < size_t(size)) return;
uint8_t check_ab;
buffer.read_byte(&check_ab);
if (check_ab == 0xAB)
{
uint8_t type;
buffer.read_byte(&type);
if (type == 0x82)
{
#ifdef DEBUG
printf("[DW]: [lobby]: received client_auth.\n");
#endif
std::string packet_3(packet.data(), packet.size() - 8); // this 8 are client hash check?
demonware::queue_packet_to_hash(packet_3);
demonware::derive_keys_s1();
char buff[14] = "\x0A\x00\x00\x00\xAB\x83";
std::memcpy(&buff[6], demonware::get_response_id().data(), 8);
std::string response(buff, 14);
raw_reply reply(response);
this->send_reply(&reply);
#ifdef DEBUG
printf("[DW]: [lobby]: sending server_auth_done.\n");
#endif
return;
}
else if (type == 0x85)
{
uint32_t msg_count;
buffer.read_uint32(&msg_count);
char seed[16];
buffer.read(16, &seed);
std::string enc = buffer.get_remaining();
char hash[8];
std::memcpy(hash, &(enc.data()[enc.size() - 8]), 8);
std::string dec = utils::cryptography::aes::decrypt(
std::string(enc.data(), enc.size() - 8), std::string(seed, 16),
demonware::get_decrypt_key());
byte_buffer serv(dec);
serv.set_use_data_types(false);
uint32_t serv_size;
serv.read_uint32(&serv_size);
uint8_t magic; // 0x86
serv.read_byte(&magic);
uint8_t service_id;
serv.read_byte(&service_id);
this->call_service(service_id, serv.get_remaining());
return;
}
}
printf("[DW]: [lobby]: ERROR! received unk message.\n");
return;
}
}
catch (...)
{
}
}
void lobby_server::call_service(const uint8_t id, const std::string& data)
{
const auto& it = this->services_.find(id);
if (it != this->services_.end())
{
it->second->exec_task(this, data);
}
else
{
printf("[DW]: [lobby]: missing service '%s'\n", utils::string::va("%d", id));
// return no error
byte_buffer buffer(data);
uint8_t task_id;
buffer.read_byte(&task_id);
this->create_reply(task_id)->send();
}
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "tcp_server.hpp"
#include "service_server.hpp"
#include "../service.hpp"
namespace demonware
{
class lobby_server : public tcp_server, service_server
{
public:
lobby_server(std::string name);
template <typename T>
void register_service()
{
static_assert(std::is_base_of<service, T>::value, "service must inherit from service");
auto service = std::make_unique<T>();
const uint8_t id = service->id();
this->services_[id] = std::move(service);
}
void send_reply(reply* data) override;
private:
std::unordered_map<uint8_t, std::unique_ptr<service>> services_;
void handle(const std::string& packet) override;
void call_service(uint8_t id, const std::string& data);
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "../reply.hpp"
namespace demonware
{
class service_server
{
public:
virtual ~service_server() = default;
virtual std::shared_ptr<remote_reply> create_message(uint8_t type)
{
auto reply = std::make_shared<remote_reply>(this, type);
return reply;
}
virtual std::shared_ptr<service_reply> create_reply(uint8_t type, uint32_t error = 0)
{
auto reply = std::make_shared<service_reply>(this, type, error);
return reply;
}
virtual void send_reply(reply* data) = 0;
};
}

View File

@ -0,0 +1,62 @@
#include <std_include.hpp>
#include "stun_server.hpp"
#include "../byte_buffer.hpp"
namespace demonware
{
void stun_server::handle(const endpoint_data& endpoint, const std::string& packet)
{
uint8_t type, version, padding;
byte_buffer buffer(packet);
buffer.set_use_data_types(false);
buffer.read_byte(&type);
buffer.read_byte(&version);
buffer.read_byte(&padding);
switch (type)
{
case 30:
this->ip_discovery(endpoint);
break;
case 20:
this->nat_discovery(endpoint);
break;
default:
break;
}
}
void stun_server::ip_discovery(const endpoint_data& endpoint)
{
const uint32_t ip = 0x0100007f;
byte_buffer buffer;
buffer.set_use_data_types(false);
buffer.write_byte(31); // type
buffer.write_byte(2); // version
buffer.write_byte(0); // version
buffer.write_uint32(ip); // external ip
buffer.write_uint16(3074); // port
this->send(endpoint, buffer.get_buffer());
}
void stun_server::nat_discovery(const endpoint_data& endpoint)
{
const uint32_t ip = 0x0100007f;
byte_buffer buffer;
buffer.set_use_data_types(false);
buffer.write_byte(21); // type
buffer.write_byte(2); // version
buffer.write_byte(0); // version
buffer.write_uint32(ip); // external ip
buffer.write_uint16(3074); // port
buffer.write_uint32(this->get_address()); // server ip
buffer.write_uint16(3074); // server port
this->send(endpoint, buffer.get_buffer());
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "udp_server.hpp"
namespace demonware
{
class stun_server : public udp_server
{
public:
using udp_server::udp_server;
private:
void handle(const endpoint_data& endpoint, const std::string& packet) override;
void ip_discovery(const endpoint_data& endpoint);
void nat_discovery(const endpoint_data& endpoint);
};
}

View File

@ -0,0 +1,84 @@
#include <std_include.hpp>
#include "tcp_server.hpp"
namespace demonware
{
void tcp_server::handle_input(const char* buf, size_t size)
{
in_queue_.access([&](data_queue& queue)
{
queue.emplace(buf, size);
});
}
size_t tcp_server::handle_output(char* buf, size_t size)
{
if (out_queue_.get_raw().empty())
{
return 0;
}
return out_queue_.access<size_t>([&](stream_queue& queue)
{
for (size_t i = 0; i < size; ++i)
{
if (queue.empty())
{
return i;
}
buf[i] = queue.front();
queue.pop();
}
return size;
});
}
bool tcp_server::pending_data()
{
return !this->out_queue_.get_raw().empty();
}
void tcp_server::frame()
{
if (this->in_queue_.get_raw().empty())
{
return;
}
while (true)
{
std::string packet{};
const auto result = this->in_queue_.access<bool>([&](data_queue& queue)
{
if (queue.empty())
{
return false;
}
packet = std::move(queue.front());
queue.pop();
return true;
});
if (!result)
{
break;
}
this->handle(packet);
}
}
void tcp_server::send(const std::string& data)
{
out_queue_.access([&](stream_queue& queue)
{
for (const auto& val : data)
{
queue.push(val);
}
});
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "base_server.hpp"
#include <utils/concurrency.hpp>
namespace demonware
{
class tcp_server : public base_server
{
public:
using base_server::base_server;
void handle_input(const char* buf, size_t size);
size_t handle_output(char* buf, size_t size);
bool pending_data();
void frame() override;
protected:
virtual void handle(const std::string& data) = 0;
void send(const std::string& data);
private:
utils::concurrency::container<data_queue> in_queue_;
utils::concurrency::container<stream_queue> out_queue_;
};
}

View File

@ -0,0 +1,103 @@
#include <std_include.hpp>
#include "udp_server.hpp"
namespace demonware
{
void udp_server::handle_input(const char* buf, size_t size, endpoint_data endpoint)
{
in_queue_.access([&](in_queue& queue)
{
in_packet p;
p.data = std::string{buf, size};
p.endpoint = std::move(endpoint);
queue.emplace(std::move(p));
});
}
size_t udp_server::handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen)
{
return out_queue_.access<size_t>([&](socket_queue_map& map) -> size_t
{
const auto entry = map.find(socket);
if (entry == map.end())
{
return 0;
}
auto& queue = entry->second;
if (queue.empty())
{
return 0;
}
auto data = std::move(queue.front());
queue.pop();
const auto copy_size = std::min(size, data.data.size());
std::memcpy(buf, data.data.data(), copy_size);
std::memcpy(address, &data.address, sizeof(data.address));
*addrlen = sizeof(data.address);
return copy_size;
});
}
bool udp_server::pending_data(SOCKET socket)
{
return this->out_queue_.access<bool>([&](const socket_queue_map& map)
{
const auto entry = map.find(socket);
if (entry == map.end())
{
return false;
}
return !entry->second.empty();
});
}
void udp_server::send(const endpoint_data& endpoint, std::string data)
{
out_queue_.access([&](socket_queue_map& map)
{
out_packet p;
p.data = std::move(data);
p.address = endpoint.address;
map[endpoint.socket].emplace(std::move(p));
});
}
void udp_server::frame()
{
if (this->in_queue_.get_raw().empty())
{
return;
}
while (true)
{
in_packet packet{};
const auto result = this->in_queue_.access<bool>([&](in_queue& queue)
{
if (queue.empty())
{
return false;
}
packet = std::move(queue.front());
queue.pop();
return true;
});
if (!result)
{
break;
}
this->handle(packet.endpoint, std::move(packet.data));
}
}
}

View File

@ -0,0 +1,62 @@
#pragma once
#include "base_server.hpp"
#include <utils/concurrency.hpp>
namespace demonware
{
class udp_server : public base_server
{
public:
struct endpoint_data
{
SOCKET socket{};
sockaddr_in address{};
endpoint_data() = default;
endpoint_data(const SOCKET sock, const sockaddr* addr, const int size)
{
if (size != sizeof(this->address))
{
throw std::runtime_error("Invalid size");
}
this->socket = sock;
std::memcpy(&this->address, addr, sizeof(this->address));
}
};
using base_server::base_server;
void handle_input(const char* buf, size_t size, endpoint_data endpoint);
size_t handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen);
bool pending_data(SOCKET socket);
void frame() override;
protected:
virtual void handle(const endpoint_data& endpoint, const std::string& data) = 0;
void send(const endpoint_data& endpoint, std::string data);
private:
struct in_packet
{
std::string data;
endpoint_data endpoint;
};
struct out_packet
{
std::string data;
sockaddr_in address;
};
using in_queue = std::queue<in_packet>;
using out_queue = std::queue<out_packet>;
using socket_queue_map = std::unordered_map<SOCKET, out_queue>;
utils::concurrency::container<in_queue> in_queue_;
utils::concurrency::container<socket_queue_map> out_queue_;
};
}

View File

@ -0,0 +1,11 @@
#include <std_include.hpp>
#include "umbrella_server.hpp"
namespace demonware
{
void umbrella_server::handle(const std::string& packet)
{
// TODO:
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "tcp_server.hpp"
namespace demonware
{
class umbrella_server : public tcp_server
{
public:
using tcp_server::tcp_server;
private:
void handle(const std::string& packet) override;
};
}

View File

@ -0,0 +1,89 @@
#pragma once
#include <utils/string.hpp>
#include "servers/service_server.hpp"
namespace demonware
{
class service
{
using callback_t = std::function<void(service_server*, byte_buffer*)>;
uint8_t id_;
std::string name_;
std::mutex mutex_;
uint8_t task_id_;
std::map<uint8_t, callback_t> tasks_;
public:
virtual ~service() = default;
service(service&&) = delete;
service(const service&) = delete;
service& operator=(const service&) = delete;
service(const uint8_t id, std::string name) : id_(id), name_(std::move(name)), task_id_(0)
{
}
uint8_t id() const
{
return this->id_;
}
const std::string& name() const
{
return this->name_;
}
uint8_t task_id() const
{
return this->task_id_;
}
virtual void exec_task(service_server* server, const std::string& data)
{
std::lock_guard<std::mutex> _(this->mutex_);
byte_buffer buffer(data);
buffer.read_byte(&this->task_id_);
const auto& it = this->tasks_.find(this->task_id_);
if (it != this->tasks_.end())
{
#ifdef DEBUG
printf("[DW] %s: executing task '%d'\n", name_.data(), this->task_id_);
#endif
it->second(server, &buffer);
}
else
{
printf("[DW] %s: missing task '%d'\n", name_.data(), this->task_id_);
// return no error
server->create_reply(this->task_id_)->send();
}
}
protected:
template <typename Class, typename T, typename... Args>
void register_task(const uint8_t id, T (Class::* callback)(Args ...) const)
{
this->tasks_[id] = [this, callback](Args ... args) -> T
{
return (reinterpret_cast<Class*>(this)->*callback)(args...);
};
}
template <typename Class, typename T, typename... Args>
void register_task(const uint8_t id, T (Class::* callback)(Args ...))
{
this->tasks_[id] = [this, callback](Args ... args) -> T
{
return (reinterpret_cast<Class*>(this)->*callback)(args...);
};
}
};
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "bit_buffer.hpp"
#include "byte_buffer.hpp"
#include "data_types.hpp"
#include "reply.hpp"
#include "service.hpp"
#include "servers/service_server.hpp"
//#include "services/bdTeams.hpp" // 3
#include "services/bdStats.hpp" // 4
//#include "services/bdMessaging.hpp" // 6
#include "services/bdProfiles.hpp" // 8
#include "services/bdStorage.hpp" // 10
#include "services/bdTitleUtilities.hpp" // 12
#include "services/bdBandwidthTest.hpp" // 18
//#include "services/bdMatchMaking.hpp" // 21
#include "services/bdCounters.hpp" // 23
#include "services/bdDML.hpp" // 27
#include "services/bdGroups.hpp" // 28
//#include "services/bdCMail.hpp" // 29
#include "services/bdFacebook.hpp" // 36
#include "services/bdAnticheat.hpp" // 38
#include "services/bdContentStreaming.hpp" // 50
//#include "services/bdTags.hpp" // 52
#include "services/bdUNK63.hpp" // 63
#include "services/bdEventLog.hpp" // 67
#include "services/bdRichPresence.hpp" // 68
//#include "services/bdTitleUtilities2.hpp" // 72
#include "services/bdUNK80.hpp"
// AccountLinking // 86
#include "services/bdPresence.hpp" //103
#include "services/bdMarketingComms.hpp" //104
#include "services/bdMatchMaking2.hpp" //138
#include "services/bdMarketing.hpp" //139

View File

@ -0,0 +1,25 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdAnticheat::bdAnticheat() : service(38, "bdAnticheat")
{
this->register_task(2, &bdAnticheat::unk2);
this->register_task(4, &bdAnticheat::report_console_details);
}
void bdAnticheat::unk2(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO: Read data as soon as needed
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdAnticheat::report_console_details(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO: Read data as soon as needed
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,14 @@
#pragma once
namespace demonware
{
class bdAnticheat final : public service
{
public:
bdAnticheat();
private:
void unk2(service_server* server, byte_buffer* buffer) const;
void report_console_details(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,27 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
static uint8_t bandwidth_iw6[51] =
{
0x0F, 0xC1, 0x1C, 0x37, 0xB8, 0xEF, 0x7C, 0xD6, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xD0, 0x07,
0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0xF4, 0x01,
0x00, 0x00, 0x02, 0x0C, 0x88, 0xB3, 0x04, 0x65, 0x89, 0xBF, 0xC3, 0x6A,
0x27, 0x94, 0xD4, 0x8F
};
bdBandwidthTest::bdBandwidthTest() : service(18, "bdBandwidthTest")
{
}
void bdBandwidthTest::exec_task(service_server* server, const std::string& data)
{
byte_buffer buffer;
buffer.write(sizeof bandwidth_iw6, bandwidth_iw6);
auto reply = server->create_message(5);
reply->send(&buffer, true);
}
}

View File

@ -0,0 +1,13 @@
#pragma once
namespace demonware
{
class bdBandwidthTest final : public service
{
public:
bdBandwidthTest();
private:
void exec_task(service_server* server, const std::string& data) override;
};
}

View File

@ -0,0 +1,25 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdContentStreaming::bdContentStreaming() : service(50, "bdContentStreaming")
{
this->register_task(2, &bdContentStreaming::unk2);
this->register_task(3, &bdContentStreaming::unk3);
}
void bdContentStreaming::unk2(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdContentStreaming::unk3(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,14 @@
#pragma once
namespace demonware
{
class bdContentStreaming final : public service
{
public:
bdContentStreaming();
private:
void unk2(service_server* server, byte_buffer* buffer) const;
void unk3(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,25 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdCounters::bdCounters() : service(23, "bdCounters")
{
this->register_task(1, &bdCounters::unk1);
this->register_task(2, &bdCounters::unk2);
}
void bdCounters::unk1(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdCounters::unk2(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,14 @@
#pragma once
namespace demonware
{
class bdCounters final : public service
{
public:
bdCounters();
private:
void unk1(service_server* server, byte_buffer* buffer) const;
void unk2(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,28 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdDML::bdDML() : service(27, "bdDML")
{
this->register_task(2, &bdDML::get_user_raw_data);
}
void bdDML::get_user_raw_data(service_server* server, byte_buffer* /*buffer*/) const
{
auto result = new bdDMLRawData;
result->country_code = "US";
result->country_code = "'Murica";
result->region = "New York";
result->city = "New York";
result->latitude = 0;
result->longitude = 0;
result->asn = 0x2119;
result->timezone = "+01:00";
auto reply = server->create_reply(this->task_id());
reply->add(result);
reply->send();
}
}

View File

@ -0,0 +1,13 @@
#pragma once
namespace demonware
{
class bdDML final : public service
{
public:
bdDML();
private:
void get_user_raw_data(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,17 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdEventLog::bdEventLog() : service(67, "bdEventLog")
{
this->register_task(6, &bdEventLog::unk6);
}
void bdEventLog::unk6(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,13 @@
#pragma once
namespace demonware
{
class bdEventLog final : public service
{
public:
bdEventLog();
private:
void unk6(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,41 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdFacebook::bdFacebook() : service(36, "bdFacebook")
{
this->register_task(1, &bdFacebook::unk1);
this->register_task(3, &bdFacebook::unk3);
this->register_task(7, &bdFacebook::unk7);
this->register_task(8, &bdFacebook::unk8);
}
void bdFacebook::unk1(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdFacebook::unk3(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdFacebook::unk7(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdFacebook::unk8(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,16 @@
#pragma once
namespace demonware
{
class bdFacebook final : public service
{
public:
bdFacebook();
private:
void unk1(service_server* server, byte_buffer* buffer) const;
void unk3(service_server* server, byte_buffer* buffer) const;
void unk7(service_server* server, byte_buffer* buffer) const;
void unk8(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,25 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdGroups::bdGroups() : service(28, "bdGroup")
{
this->register_task(1, &bdGroups::set_groups);
this->register_task(4, &bdGroups::unk4);
}
void bdGroups::set_groups(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdGroups::unk4(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

View File

@ -0,0 +1,14 @@
#pragma once
namespace demonware
{
class bdGroups final : public service
{
public:
bdGroups();
private:
void set_groups(service_server* server, byte_buffer* buffer) const;
void unk4(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -0,0 +1,25 @@
#include <std_include.hpp>
#include "../services.hpp"
namespace demonware
{
bdMarketing::bdMarketing() : service(139, "bdMarketing")
{
this->register_task(2, &bdMarketing::unk2);
this->register_task(3, &bdMarketing::unk3);
}
void bdMarketing::unk2(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
void bdMarketing::unk3(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply->send();
}
}

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