Board logo

标题: [技术讨论] PowerShell看RAR分割檔裡的mp4(缺檔) [打印本页]

作者: nwm310    时间: 2016-11-4 21:35     标题: PowerShell看RAR分割檔裡的mp4(缺檔)

以下方法使用條件:
        壓縮方式:僅儲存
        至少要有:第一檔
                最後一檔(mp4的重要資訊在檔尾。如果最後一檔太小,可能需要倒數第二檔)

方法1:個別解出來、再合併

        7-Zip可以個別解出每個分割檔的內容
        計算「缺檔大小」 = mp4大小 - 已解出的總合
        產生符合「缺檔大小」的檔案,再依順序合併檔案

        也許可以用到 fsutil file
                     fsutil sparse
                        (沒測試)
方法2:自己補檔

        缺少的是中間檔(第一檔、最後一檔以外的檔案)
        並且有其他中間檔存在

        例如:目前有 part1  part2  part4  part5
                缺少 part3

        把其中一個中間檔複製並改名成「缺檔的檔名」
        例如:
                把part2 複製並改名成part3

        然後,就可以用MPC-HC、VLC player、Potplayer來看


方法3:掛載(mount)RAR分割檔

需要 PowerShell 5.0

分割檔限制:
        壓縮方式:僅儲存
        壓縮檔格式:RAR (不是新的RAR5)
        壓縮時所設定的分割檔大小:每一個分割檔都一樣大 (除了最後一檔)
        下載後的分割檔大小:可以下載到一半、沒有下載完成
        不能處理檔名加密的RAR檔(一般加密可以處理)

        至少要有:第一檔
                最後一檔(mp4的重要資訊在檔尾。如果最後一檔太小,可能需要倒數第二檔)



下載:
        到http://pismotec.com/download/
        下載並安裝 Pismo File Mount Audit Package

        下載Pismo File Mount Developer Kit
        取得pfmclr_183.dll、pfmshim16_183.dll
        分別在這兩個dll 按右鍵 → 內容 → 解除封鎖

執行方式:
        pfmclr_183.dll
        pfmshim16_183.dll
        RAR分割檔
        ps1

        把這些東西放在一起,執行 ps1 (不要用「管理員權限」開啟)

執行之後:
        要自己去找新掛載的磁碟機(它不會自己彈出來)
        如果沒加密,磁碟機裡的是mp4,可以直接打開
        如果有加密,磁碟機裡的是rar,要用WinRAR開啟 (會出現錯誤訊息)
                                        解壓縮 (保留毀損的檔案)


卸載:
        關掉PS視窗就卸載了。
        卸載方面有問題。
        卸載之後,打開Powershell視窗,會出現
                嘗試在 'FileSystem' 提供者上執行 InitializeDefaultDrives 作業失敗。

        如果要完全卸載,請重新開機
  1. #Requires -version 5.0
  2. if ($host.Version.Major -lt 5){write-host '需要 PowerShell 5.0';pause;exit}
  3. $dllfiles = @(gi pfmclr_183.dll , pfmshim16_183.dll )
  4. if ( $dllfiles.length -lt 2 ){
  5. write-host '需要dll';pause;exit
  6. }else{
  7. Add-Type  -literal  $dllfiles[0]
  8. }
  9. #分析RAR================================================================================
  10. $rarGroup = dir *.part*.rar | sort |?{$_.name -match '(.*)\.part(\d+)\.rar$' } |
  11. group -prop {$matches[1] + '/' + $matches[2].length}
  12. for ($i = 0 ; $i -lt $rarGroup.length; $i++){
  13. if ($rarGroup[$i].group[0] -match 't0*1\.rar$'){
  14. $rar = $rarGroup[$i].group ; $nFiles = $rarGroup[$i].Count ; break}
  15. }
  16. if ($i -eq $rarGroup.lenth) {write-host '找不到第一檔';pause;exit}
  17. if ($nFiles -eq 1) {write-host '只有一檔,不用處理了';pause;exit}
  18. $fs = $rar[0].OpenRead()
  19. $buffer = new-object byte[](2kb)
  20. $n = $fs.read($buffer , 0 , 20)
  21. if ( $n -ne 20 ) {$fs.close();write-host '第一檔太小了';pause;exit}
  22. if ($buffer[10] -band 0x80){
  23. $fs.close();write-host '不能處理檔名加密的RAR檔';pause;exit
  24. }
  25. $findOK = $false
  26. while ($fs.Position -lt $fs.length ){
  27. $n = $fs.read($buffer , 20 , 7)
  28. if ( $n -ne 7 ) {$fs.close();write-host '第一檔太小了';pause;stop-process -Id $PID}
  29. $n = [BitConverter]::ToUInt16($buffer , 25) -7
  30. if ( ($fs.Position + $n ) -gt $fs.length ){
  31. $fs.close();write-host '第一檔太小了';pause;stop-process -Id $PID
  32. }
  33. [void]$fs.read($buffer , 27 , $n)
  34. #如果是檔案區塊
  35. if ( $buffer[22] -eq 0x74 ) {
  36. #如果是mp4 -->  file continued in next volume
  37. if ( $buffer[23] -band 2 ) {
  38. #有沒有加密
  39. if ( $buffer[23] -band 4 ) { $encryption = $true} else { $encryption = $false}
  40. if ( $buffer[45] -ne 0x30) {
  41. $fs.close();write-host '壓縮方式不是僅儲存';pause
  42. stop-process -Id $PID
  43. }
  44. $HeaderSize = $n + 7
  45. $FirstSize = [BitConverter]::ToUInt32($buffer, 27 )
  46. $Mp4Size = [BitConverter]::ToUInt32($buffer, 31 )
  47. #如果檔案很大
  48. if  ($buffer[24] -band 1 ){
  49. $HighSize = [BitConverter]::ToUInt32($buffer, 52 )
  50. $FirstSize = ([long]$HighSize -shl 32 ) + $FirstSize
  51. $HighSize = [BitConverter]::ToUInt32($buffer, 56 )
  52. $Mp4Size = ([long]$HighSize -shl 32 ) + $Mp4Size
  53. }
  54. #取得檔名
  55. $nameSize = [BitConverter]::ToUInt16($buffer , 46 )
  56. if  ($buffer[24] -band 1 ){$namePos = 60 }else { $namePos = 52 }
  57. $mp4Name=[System.Text.Encoding]::Default.GetString($buffer,$namePos,$nameSize)
  58. $mp4Name = $mp4Name -replace '\x00.*',''  -replace '\?','_'
  59. $FirstOffset = $fs.Position
  60. $findOK = $true
  61. break
  62. }else{ #第一檔裡面的其他檔案
  63. $n = [BitConverter]::ToUInt32($buffer, 27)
  64. if  ($buffer[24] -band 1 ) {
  65. $HighSize = [BitConverter]::ToUInt32($buffer, 52 )
  66. $n += ([long]$HighSize -shl 32 )
  67. }
  68. if ( ($fs.Position + $n ) -gt $fs.length ){
  69. $fs.close();write-host '第一檔太小了';pause;stop-process -Id $PID
  70. }
  71. $fs.Position += $n
  72. }
  73. }else{#非檔案區塊
  74. if ($buffer[24] -band 0x80){
  75. $n = [BitConverter]::ToUInt32($buffer, 27)
  76. if ( ($fs.Position + $n ) -gt $fs.length ){
  77. $fs.close();write-host '第一檔太小了';pause;stop-process -Id $PID
  78. }
  79. $fs.Position += $n
  80. }
  81. }
  82. }
  83. $fs.close()
  84. if ( !$findOK) {write-host '沒找到mp4';pause;exit}
  85. #計算RAR資訊================================================================================
  86. $MidOffset = $LastOffset = $HeaderSize + 20
  87. $MidSize = $FirstSize + ( $FirstOffset - $MidOffset )
  88. $FullPack = $Mp4Size
  89. if ($encryption){
  90. if ( $Mp4Size % 16 ) { $FullPack += 16 - ($Mp4Size % 16) }
  91. }
  92. $LastSize = ($FullPack - $FirstSize) % $MidSize
  93. $nLastPart = ($FullPack - $FirstSize - $LastSize) / $MidSize +2
  94. $Mp4offset = $nEmpty = 0
  95. $mp4Info = & {
  96. if ($encryption){
  97. $buffer[10] = $buffer[10] -band 238
  98. $buffer[11] = $buffer[11] -band 254
  99. $buffer[23] = $buffer[23] -band 253
  100. $fpbyte = [BitConverter]::GetBytes([uint64]$FullPack)
  101. [array]::copy($fpbyte,0,$buffer,27,4)
  102. if  ($buffer[24] -band 1 ){
  103. [array]::copy($fpbyte,4,$buffer,52,4)
  104. }
  105. @{fs = new-object IO.MemoryStream($buffer,0, (20 + $HeaderSize))
  106. Mp4offset = $Mp4offset ;offset = 0 ; length = 20 + $HeaderSize
  107. }
  108. $Mp4offset += 20 + $HeaderSize
  109. }
  110. if (  ($FirstOffset + $FirstSize) -gt $rar[0].length  ) {
  111. $nEmpty = $FirstOffset + $FirstSize - $rar[0].length
  112. }
  113. @{fs = $rar[0].OpenRead() ; Mp4offset = $Mp4offset
  114. offset = $FirstOffset ; length = $FirstSize - $nEmpty
  115. }
  116. $Mp4offset += $FirstSize - $nEmpty
  117. $nextN = 2
  118. #$rar[index]
  119. 1.. ($nFiles - 1) | %{
  120. [int]$nPart = $rar[$_].basename -replace '.*part',''
  121. if ( $nPart -gt $nLastPart ) {
  122. @{fs = $null ; Mp4offset = $Mp4offset ; length = $FullPack - $Mp4offset}
  123. $nPart = $nLastPart ; return
  124. }
  125. if ( $nPart -gt $nextN  -or  $nEmpty -gt 0 ){
  126. @{fs = $null ; Mp4offset = $Mp4offset
  127. length = $MidSize * ( $nPart - $nextN ) +  $nEmpty
  128. }
  129. $Mp4offset += $MidSize * ( $nPart - $nextN ) +  $nEmpty
  130. $nEmpty = 0
  131. }
  132. #如果是最後一檔
  133. if ( $nPart -eq $nLastPart ){
  134. if (  ($LastOffset + $LastSize) -gt $rar[$_].length  ) {
  135. $nEmpty = $LastOffset + $LastSize - $rar[$_].length
  136. }
  137. @{fs = $rar[$_].OpenRead() ; Mp4offset = $Mp4offset
  138. offset = $LastOffset ; length = $LastSize - $nEmpty
  139. }
  140. if ( $nEmpty -gt 0 ){
  141. $Mp4offset += $LastSize - $nEmpty
  142. @{fs = $null ; Mp4offset = $Mp4offset ; length = $nEmpty}
  143. }
  144. return
  145. }else{#中間檔
  146. if (  ($MidOffset + $MidSize) -gt $rar[$_].length  ) {
  147. $nEmpty = $MidOffset + $MidSize - $rar[$_].length
  148. }
  149. @{fs = $rar[$_].OpenRead() ; Mp4offset = $Mp4offset
  150. offset = $MidOffset ; length = $MidSize - $nEmpty
  151. }
  152. $Mp4offset += $MidSize - $nEmpty
  153. $nextN = $nPart + 1
  154. }
  155. }
  156. if ( $nPart -lt $nLastPart ){
  157. @{fs = $null ; Mp4offset = $Mp4offset ; length = $FullPack - $Mp4offset}
  158. }
  159. }
  160. #====================================================================================
  161. $RarVolumeType=@'
  162. #Learn from   Pismo File Mount Development Kit   samples\hellofs_cs\hellofs.cs
  163. class RarVolume: Pfm+FormatterDispatch{
  164. $openRoot
  165. $openFile1
  166. $File1_name
  167. $File1_info
  168. RarVolume(){
  169. $this.openRoot = new-object Pfm+OpenAttribs -prop @{
  170. openSequence = 1 ; accessLevel = [Pfm]::accessLevelReadData;
  171. attribs = new-object Pfm+Attribs -prop @{fileType = [Pfm]::fileTypeFolder ; fileId = 2 }}
  172. $this.openFile1 = new-object Pfm+OpenAttribs -prop @{
  173. openSequence = 1 ; accessLevel = [Pfm]::accessLevelReadData;
  174. attribs = new-object Pfm+Attribs -prop @{fileType = [Pfm]::fileTypeFile ; fileId = 3 }}
  175. }
  176. [void] Dispose(){}
  177. [void] Open( [Pfm+MarshallerOpenOp]$op){
  178. $perr = 0
  179. $existed = $false
  180. $parentFileId = 0
  181. $endName = $null
  182. $openAttribs =  new-object Pfm+OpenAttribs
  183. if ($op.NameParts().Length -eq 0){
  184. if ($this.openRoot.openId -eq 0) {$this.openRoot.openId = $op.NewExistingOpenId() }
  185. $existed = $true
  186. $openAttribs = $this.openRoot
  187. }elseif($op.NameParts().Length -eq 1){
  188. if($op.NameParts()[0].ToLowerInvariant() -ne $this.File1_name.ToLowerInvariant()){
  189. $perr = [Pfm]::errorNotFound
  190. }else{
  191. if ($this.openFile1.openId -eq 0) {$this.openFile1.openId = $op.NewExistingOpenId() }
  192. $existed = $true
  193. $endName = $this.File1_name
  194. $openAttribs = $this.openFile1
  195. }
  196. }else{  $perr = [Pfm]::errorParentNotFound }
  197. if($perr -eq [Pfm]::errorNotFound -and $op.CreateFileType() -ne 0)
  198. { $perr = [Pfm]::errorAccessDenied          }
  199. $op.Complete( $perr, $existed, $openAttribs, $parentFileId, $endName, 0, $null, 0, $null)
  200. }
  201. [void] Replace( [Pfm+MarshallerReplaceOp]$op){
  202. $op.Complete( [Pfm]::errorAccessDenied, $null, $null)
  203. }
  204. [void] Move( [Pfm+MarshallerMoveOp] $op){
  205. $op.Complete( [Pfm]::errorAccessDenied, $false, $null, 0, $null, 0, $null, 0, $null)
  206. }
  207. [void] MoveReplace( [Pfm+MarshallerMoveReplaceOp] $op){
  208. $op.Complete( [Pfm]::errorAccessDenied)
  209. }
  210. [void] Delete( [Pfm+MarshallerDeleteOp] $op){
  211. $op.Complete( [Pfm]::errorAccessDenied)
  212. }
  213. [void] Close( [Pfm+MarshallerCloseOp] $op){
  214. $op.Complete( [Pfm]::errorSuccess)
  215. }
  216. [void] FlushFile( [Pfm+MarshallerFlushFileOp] $op){
  217. $perr = 0
  218. $openAttribs = new-object Pfm+OpenAttribs
  219. if ( $op.FileFlags() -ne [Pfm]::fileFlagsInvalid -or
  220.          $op.Color() -ne [Pfm]::colorInvalid -or
  221.           $op.CreateTime() -ne [Pfm]::timeInvalid -or
  222.           $op.WriteTime() -ne [Pfm]::timeInvalid){
  223. $perr = [Pfm]::errorAccessDenied
  224. }elseif ($op.OpenId() -eq $this.openRoot.openId){
  225. $openAttribs = $this.openRoot
  226. }elseif ($op.OpenId() -eq $this.openFile1.openId){
  227. $openAttribs = $this.openFile1
  228. }else{
  229. $perr = [Pfm]::errorNotFound
  230. }
  231. $op.Complete( $perr, $openAttribs, $null)
  232. }
  233. [void] List( [Pfm+MarshallerListOp] $op){
  234. $perr = 0
  235. if ($op.OpenId() -ne $this.openRoot.openId){
  236. $perr = [Pfm]::errorAccessDenied
  237. }else{
  238. $op.Add( $this.openFile1.attribs, $this.File1_name)
  239. }
  240. $op.Complete( $perr, $true)
  241. }
  242. [void] ListEnd( [Pfm+MarshallerListEndOp] $op){
  243. $op.Complete( [Pfm]::errorSuccess)
  244. }
  245. [void] Read( [Pfm+MarshallerReadOp] $op){
  246. $data = $op.Data()
  247. $perr = 0
  248. $actualSize = 0
  249. if ($op.OpenId() -ne $this.openFile1.openId){
  250. $perr = [Pfm]::errorAccessDenied
  251. }else{
  252. $actualSize = $op.RequestedSize()
  253. if ($op.FileOffset() -ge $this.openFile1.attribs.fileSize){
  254. $actualSize = 0
  255. }elseif ( (  $op.FileOffset() + $op.RequestedSize()) -gt $this.openFile1.attribs.fileSize){
  256. $actualSize = $this.openFile1.attribs.fileSize - $op.FileOffset()
  257. }
  258. if ($actualSize -ne 0){
  259. for ( $i = 1 ; $i -lt $this.File1_info.length ; $i++ ){
  260. if ($op.FileOffset()  -lt $this.File1_info[$i].Mp4offset ){break}
  261. }
  262. $i--
  263. if ($this.File1_info[$i].fs -ne $null ){
  264. $this.File1_info[$i].fs.Position =
  265. $op.FileOffset() - $this.File1_info[$i].Mp4offset + $this.File1_info[$i].offset
  266. }
  267. $aaa = $this.File1_info[$i].length - ( $op.FileOffset() - $this.File1_info[$i].Mp4offset)
  268. $offset = 0
  269. $actualSize1 = $actualSize
  270. while ($actualSize1 -gt 0){
  271. if ($actualSize1 -gt $aaa ){
  272. if ($this.File1_info[$i].fs -ne $null ){
  273. $this.File1_info[$i].fs.read($data , $offset , $aaa)
  274. }else{
  275. [array]::Clear( $data , $offset , $aaa)
  276. }
  277. $actualSize1 -= $aaa
  278. $offset += $aaa
  279. $i++
  280. if ($this.File1_info[$i].fs -ne $null ){
  281. $this.File1_info[$i].fs.Position = $this.File1_info[$i].offset
  282. }
  283. $aaa = $this.File1_info[$i].length
  284. }else{
  285. if ($this.File1_info[$i].fs -ne $null ){
  286. $this.File1_info[$i].fs.read($data , $offset , $actualSize1)
  287. }else{
  288. [array]::Clear( $data , $offset , $actualSize1)
  289. }
  290. $actualSize1 = 0
  291. }
  292. }
  293. }
  294. }
  295. $op.Complete( $perr, $actualSize)
  296. }
  297. [void] Write( [Pfm+MarshallerWriteOp] $op){
  298. $op.Complete( [Pfm]::errorAccessDenied, 0)
  299. }
  300. [void] SetSize( [Pfm+MarshallerSetSizeOp] $op){
  301. $op.Complete( [Pfm]::errorAccessDenied)
  302. }
  303. [void] Capacity( [Pfm+MarshallerCapacityOp] $op){
  304. $op.Complete( [Pfm]::errorSuccess, 10TB, 9TB)
  305. }
  306. [void] FlushMedia( [Pfm+MarshallerFlushMediaOp] $op){
  307. $op.Complete( [Pfm]::errorSuccess, -1)
  308. }
  309. [void] Control( [Pfm+MarshallerControlOp] $op){
  310. $op.Complete( [Pfm]::errorInvalid, 0)
  311. }
  312. [void] MediaInfo( [Pfm+MarshallerMediaInfoOp] $op){
  313. $mediaInfo = new-object Pfm+MediaInfo
  314. $op.Complete( [Pfm]::errorSuccess, $mediaInfo, "RarMp4")
  315. }
  316. [void] Access( [Pfm+MarshallerAccessOp] $op){
  317. $perr = 0
  318. $openAttribs =  new-object Pfm+OpenAttribs
  319. if ($op.OpenId() -eq $this.openRoot.openId){
  320. $openAttribs = $this.openRoot
  321. }elseif($op.OpenId() -eq $this.openFile1.openId){
  322. $openAttribs = $this.openFile1
  323. }else{
  324. $perr = [Pfm]::errorNotFound
  325. }
  326. $op.Complete( $perr, $openAttribs, $null)
  327. }
  328. [void] ReadXattr( [Pfm+MarshallerReadXattrOp] $op){
  329. $op.Complete( [Pfm]::errorNotFound, 0, 0)
  330. }
  331. [void] WriteXattr( [Pfm+MarshallerWriteXattrOp] $op){
  332. $op.Complete( [Pfm]::errorAccessDenied, 0)
  333. }
  334. }
  335. '@
  336. Invoke-Expression $RarVolumeType
  337. #開始掛載==================================================================================
  338. $mcp = new-object Pfm+MountCreateParams -prop @{
  339. mountFlags = 1
  340. driveLetter = "Z"
  341. mountSourceName = "感謝大大無私分享"  }
  342. $msp = new-object Pfm+MarshallerServeParams  -prop @{
  343. volumeFlags = 1
  344. dispatch = new-object RarVolume
  345. formatterName = "RarMp4Fs"  }
  346. $msp.dispatch.File1_name = split-path  $mp4Name  -leaf
  347. $msp.dispatch.openFile1.attribs.fileSize = $Mp4Size
  348. $msp.dispatch.File1_info = $mp4Info
  349. if ($encryption){
  350. $msp.dispatch.File1_name = $msp.dispatch.File1_name +".rar"
  351. $msp.dispatch.openFile1.attribs.fileSize = $FullPack + 20 + $HeaderSize
  352. }
  353. $n1 = $n2 = -1
  354. $err = [Pfm]::SystemCreatePipe( [ref] $n1, [ref]$n2 )
  355. $msp.toFormatterRead = $n1
  356. $mcp.toFormatterWrite = $n2
  357.       
  358. $n1 = $n2 = -1
  359. $err = [Pfm]::SystemCreatePipe( [ref] $n1, [ref]$n2 )
  360. $mcp.fromFormatterRead = $n1
  361. $msp.fromFormatterWrite = $n2        
  362. $pfmApi = $null
  363. $err = [Pfm]::ApiFactory( [ref] $pfmApi)
  364. if($err -ne 0){  Write-Host "ERROR: $err Unable to open PFM API.`n" ; pause ; exit }
  365. $mount = $null
  366. $err = $pfmApi.MountCreate( $mcp, [ref] $mount)
  367. if ($err -ne 0){ Write-Host "ERROR: $err Unable to create mount.`n"  }
  368. [Pfm]::SystemCloseFd( $mcp.toFormatterWrite)
  369. [Pfm]::SystemCloseFd( $mcp.fromFormatterRead)
  370. Write-Host "Press CTRL+C to exit.`n"
  371. $marshaller = $null
  372. $err = [Pfm]::MarshallerFactory( [ref]$marshaller )
  373. if($err -ne 0){  Write-Host "ERROR: $err Unable to create marshaller.`n" ; pause ; exit }
  374. $marshaller.ServeDispatch( $msp)
  375. [Pfm]::SystemCloseFd( $msp.toFormatterRead)
  376. [Pfm]::SystemCloseFd( $msp.fromFormatterWrite)
  377. $pfmApi.Dispose()
  378. $mount.Dispose()
  379. $marshaller.Dispose()
复制代码





欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2