Leaderboard


Popular Content

Showing content with the highest reputation on 01/25/2021 in Posts

  1. 2 points
    Life has been crazy, and I've seen things I never thought would happen in my country. But, coding never stops. Time for an overdue update! An earlier update outlined the structure of a subroutine: return_type subA(Argument_1, Argument_2...) { //Part 1 - The code that does stuff //Part 2 - Values returned //Part 3 - Destruction of local variables //Part 4 - Destruction of arguments } Part 2 returns values from a subroutine using the CPDOWNSP opcode. Parts 3 and 4 destroy local variables and arguments using the MOVSP opcode. These opcodes can appear in any part of a subroutine. So, how do we know if we're returning a value, and destroying variables and/or arguments? We analyze part 1 to determine what, if anything, is on the stack. Computers execute operations in sequence unless directed otherwise. In high-level languages like NWScript, control statements alter the sequence. An if statement conditionally executes code. If the test condition fails, this code is jumped over. So, what does an if statement look like in the compiled NCS? 102 CPTOPSP -3 1 110 CONSTI 0 116 EQUALxx 118 JZ 170 124 CPTOPSP -1 1 132 CPTOPSP -3 1 140 CONSTO object 146 JSR 298 152 MOVSP -3 158 JMP 296 164 JMP 170 In the example, the opcode at offset 102 of the NCS file copies the value of a variable to the top of the stack. Offset 110 places the value 0 onto the stack. Offset 116 performs a test of equality of the two values, removes them from the stack, then places the result of the test onto the stack. Offset 118 performs a conditional jump based on the result, removing the result in the process. Offsets 124-164 are the body of the if statement, which consists of a call to a subroutine followed by a return statement: if ( nKillMode == 0 ) { DamagingExplosion(OBJECT_SELF, nDelay, nDamage); return; } So many ops, so few statements. That's why we write in high-level languages instead of assembly, because the compiler allows us to focus on what the program should do rather than creating the proper sequence of opcodes to keep the program from breaking. Notice in the body of the if statement there is a MOVSP. That is the destruction of local variables, part 3. There is no part 2, because the subroutine returns void (nothing). But, how do we know the MOVSP is part 3 and not part 4? And why are there two unconditional jumps??? The challenge of writing a reverse compiler is knowing the rules used to compile. I've spent the past six months observing, analyzing, and documenting those rules. For example, all control statements other than the switch statement are created using the jump-if-zero (JZ) opcode. (Switch statements are created using the jump-if-not-zero [JNZ] opcode.) However, the existence of a JZ opcode does not mean you've encountered a control statement! JZ is used to create the following: Logical AND Logical OR if statements else if statements while statements for statements do-while statements (I finally found one!!!) This means we need to some way of determining what the range of addresses created by the JZ opcode is. In the example above, the range created by the if statement is [124, 170). In this range notation a bracket "[" means the offset is included in the range, and a parenthesis ")" means the offset is outside of the range. The 170 is obvious because it's specified by the JZ opcode; if nKillMode == 0 returns false we jump to 170 and resume execution from there. Where does the 124 come from? Remember what was stated above: "computers execute operations in sequence unless directed otherwise." A JZ opcode is 6 bytes in length and the JZ is at offset 118, so 118 + 6 means the next op in the sequence is at offset 124. If nKillMode == 0 returns true we enter the body of the if statement at offset 124. Offset 124 is the first address in the range, offset 170 is the first address outside of the range. All control statements are ranges, but not all ranges are control statements. The NCS file is itself a range: [0, file size). Subroutines are ranges. However, our focus is on the ranges created by JZ. How do we determine what they are? The unconditional jump, JMP. Notice the example ends with two JMPs. This caused me lots of stress because I didn't know why they were there. I'm now convinced this is one of the rules of compiling NCS files: control statements end with a JMP. The if statement is easily identified as such because the second JMP goes to the same offset as the JZ. The true block of an if-else statement ends in a JMP to an offset greater than that of the JZ. The if and else-if statements all end in JMPs to the same offset. Loops all end in JMPs to an offset less than the offset of the JZ. Logical AND and Logical Or are not control statements, so they do not end in a JMP. Our example has two JMPs. Why? Most likely the original compiler was dumb. The NWScript return statement is translated into a JMP to the end of the subroutine, offset 296. The opcode at this offset is RETN, the last opcode in a subroutine. Anytime you see a return in NWScript that isn't the last statement in the subroutine, you know it will be implemented as a JMP: if ( nKillMode == 0 ) { return; } if ( nKillMode == 1 ) { return; } if ( nKillMode == 2 ) { return; } is 118 JZ 170 158 JMP 296 164 JMP 170 186 JZ 230 218 JMP 296 224 JMP 230 246 JZ 290 278 JMP 296 284 JMP 290 296 RETN The original compiler just stuck the body of the if statement in between the JZ and JMP. A modern compiler would have removed the second JMP since it's unreachable code. OK, this is great and all. But what about the reverse compiler? Well, now that I have the rules down I'm close to the finish line. The reverse compiler accurately finds ranges and determines what types they are. The output looks like this: This is definitely a K2 file Subroutine 13, 21 13, 21 //Subroutine Subroutine 21, 298 21, 298 //Subroutine 124, 170 //if 192, 230 //if 252, 290 //if Subroutine 298, 1345 298, 1345 //Subroutine 320, 396 //if 704, 722 //logical AND or OR 728, 1216 //loop 787, 805 //logical AND or OR 811, 1135 //if 914, 970 //if-else 1017, 1043 //if-else Subroutine 1345, 1601 1345, 1601 //Subroutine 1459, 1553 //if 1511, 1547 //if Subroutine 1601, 1637 1601, 1637 //Subroutine Subroutine 1637, 1813 1637, 1813 //Subroutine 1659, 1727 //if There's more to be done, particularly for loops because they support the break and continue statements (also implemented with JMP). But, hard part seems to be behind me. Should have an update in two weeks.
  2. 1 point
    No problem. The enthusiasm is honestly refreshing.
  3. 1 point
    I've been using better texture mods for a while now but every mod I've used has never had a better corpse texture and its always bugged me. Can anyone make one?
  4. 1 point
    Anything is possible, although I question the sanity of anyone actually wanting it. You'd pretty much just take a regular saber, add some subdivisions to the blade planes, add a solid rod down the middle with matching subdivisions, then just shuffle some rows of verts side to side to get the zig zag effect. If you wanted to keep it as a saber you'd also need to change the default scale of the central rod and add keyframes for it to the on/off anims. Texture-wise you could try a more star shaped blade plane texture to try and get a more wavy edge like the picture, but I don't know how that would turn out.
  5. 1 point
    The way I understand the chain of events, it happened as follows: The Ebon Hawk got attacked by a Sith Warship manned by Sion and his assassins, possibly because it carried Kreia (this is just speculation, though). The Harbinger with the Exile and the HK-50 on board detected it and moved to intercept. The Harbinger later tractored in the Hawk and searched the Warship, where they found Sion's 'corpse', but nothing else. However, the assassins decided to board the Harbinger, abandoning the Warship, where they started sabotaging stuff, probably in order to get to Kreia and eventually found out about the Exile. However, at around the same time, either Kreia or the HK-50 (not entirely sure, haven't played the game in a while) incapacitated the Exile and moved him/her onto the Hawk in order to escape Sion and company. T3 was probably still on board and most likely was the one who locked the HK-50 in the cargo hold. After detaching, the Ebon Hawk attempted to escape the scene, and the Harbinger, now under control of Sion, opened fire, which led to the Hawk being damaged, and presumably Kreia being injured. T3 later managed to get the Hawk to Peragus, where the HK-50 got out of his compartment, at which point the main plot begins.
  6. 1 point
    The short answer is we'll never know because the game was unfinished. It was supposed to be larger. MUCH larger. But LucasArts wanted the game released in time for Christmas, so the game's development was cut short by almost a year. The budget was cut, too. The reason is shortly before TSL came out, LucasArts was restructured and a lot of their game development was canceled. Even before the restructuring, BioWare saw trouble and passed on developing the sequel. They recommended Obsidian, who had worked with them in the past. Unfortunately for Obsidian, plotting of TSL was started BEFORE KoTOR was finished and LucasArts refused to allow Obsidian access to the developing game. When KoTOR finally came out Obsidian had to go back and redo material that didn't fit with the original game's story. They had to start over, giving them even less time to write the story. This is the reason for so many plot holes and unfinished content, which is why modders have been working to restore whatever they could find. KoTOR and TSL are examples of what happens when a company (LucasArts) has crappy management. A whole book was written about it, in fact.
  7. 1 point
    To put it quite simply and in as clear a way as possible... We do not steal others' work, we do not go behind their backs or against their wishes, we respect them and their right to what they do with their things, and we absolutely will not go back on our principles of upholding a standard and code of conduct in how we handle ourselves or treat our community.
  8. 1 point
    Please someone tell that guy that removing that file doesn't mean it's destroyed because people can simply download it from deadlystream.com.
  9. 1 point
    It goes against the Steam Subscriber Agreement
  10. 0 points
    I haven't read all of the posts yet, so I don't know if TSL's development history was addressed. It wasn't Obsidian's fault, it was LucasArts'. LucasArts requested BioWare make a sequel before KoTOR was finished, transferred development to Obsidian after BioWare refused, kept Obsidian from speaking with BioWare, and gave Obsidian a year and a half to make the game when KoTOR took three years. The first Obsidian learned about KoTOR came from playing the game after it was released, and the company had to scrap damn near everything it had and start over. Out of 18 months, 3 were lost to learning about KoTOR, and then story and game development proceeded. This is the reason why you barely see anything from KoTOR in TSL other than the game engine and a few character and object models. Even worse, TSL was supposed to be a bigger game and the Exile was supposed to get to level 50. That was no longer possible with only 15 months of development and testing. Much of what has been restored in TSLRCM is undeveloped content that was scrapped due to time constraints. The game we're playing is less than a year's worth of work. It's amazing it's as good as it is.